commit 8197a022bd53ceda2979eeb7381ae1d54b576a77 Author: AArt1256 Date: Thu Nov 13 19:07:39 2025 +0300 init files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc84e9e --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +actual code is in `loader/samples/minexample` +Uses Krill's loader \ No newline at end of file diff --git a/loader/Makefile b/loader/Makefile new file mode 100755 index 0000000..a7cac7a --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,753 @@ + +NOWORKINGCOPY = $(shell svn info > /dev/null 2>&1; echo $$?) +SVNVERSION = svnversion +ifeq ($(NOWORKINGCOPY),0) +VERSION = $(shell $(SVNVERSION) | tr -d [:cntrl:]) +else +VERSION = $(shell $(GREP) -oP 'VERSION_STRING "\K[^"]+' ./version.inc) +endif + +CD = cd +PRINTF = printf +MKDIR = mkdir +GREP = ggrep +ZIP = zip -9 --must-match +ZIP_NO_PATHS = $(ZIP) -j +ifneq ($(VERSION),) +ZIPFILE = loader-v$(VERSION).zip +LOADERTEST = loadertest-v$(VERSION).zip +else +ZIPFILE = loader.zip +LOADERTEST = loadertest.zip +endif + + +SHAREDFOLDER = shared +LOADERFOLDER = $(notdir $(shell pwd)) +DOCSFOLDER = $(LOADERFOLDER)/docs +SRCFOLDER = $(LOADERFOLDER)/src +INCFOLDER = $(LOADERFOLDER)/include +SAMPLESFOLDER = $(LOADERFOLDER)/samples +BUILDFOLDER = $(LOADERFOLDER)/build +TOOLSFOLDER = $(LOADERFOLDER)/tools + +ZIPLIST += $(LOADERFOLDER)/README + +ZIPLIST += $(SHAREDFOLDER)/standard.inc +ZIPLIST += $(SHAREDFOLDER)/cpu.inc +ZIPLIST += $(SHAREDFOLDER)/vic.inc +ZIPLIST += $(SHAREDFOLDER)/cia.inc +ZIPLIST += $(SHAREDFOLDER)/mmu.inc +ZIPLIST += $(SHAREDFOLDER)/vdc.inc +ZIPLIST += $(SHAREDFOLDER)/ted.inc +ZIPLIST += $(SHAREDFOLDER)/pio.inc +ZIPLIST += $(SHAREDFOLDER)/via.inc +ZIPLIST += $(SHAREDFOLDER)/kernal.inc +ZIPLIST += $(SHAREDFOLDER)/basic.inc +ZIPLIST += $(SHAREDFOLDER)/float.inc + +ZIPLIST += $(LOADERFOLDER)/Makefile +ZIPLIST += $(LOADERFOLDER)/version.inc + +ZIPLIST += $(DOCSFOLDER)/Prerequisites.txt +ZIPLIST += $(DOCSFOLDER)/Protocol.txt + +DECOMPFOLDER = decompress +DRIVESFOLDER = drives +HALFOLDER = hal +ZIPLIST += $(SRCFOLDER)/Makefile +ZIPLIST += $(SRCFOLDER)/make-linkfile.pl +ZIPLIST += $(SRCFOLDER)/make-loadersymbolsinc.pl +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/b2decomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/bitnaxdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/doynaxdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/exodecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/lcdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/lzsa2decomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/ncdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/pudecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/subsizerdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/tcdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/tsdecomp.s +ZIPLIST += $(SRCFOLDER)/$(DECOMPFOLDER)/zx0decomp.s +ZIPLIST += $(SRCFOLDER)/$(DRIVESFOLDER)/drivecode-common.inc +ZIPLIST += $(SRCFOLDER)/$(DRIVESFOLDER)/drivecode1541.s +ZIPLIST += $(SRCFOLDER)/$(DRIVESFOLDER)/drivecode1571.s +ZIPLIST += $(SRCFOLDER)/$(DRIVESFOLDER)/drivecode1581.s +ZIPLIST += $(SRCFOLDER)/$(HALFOLDER)/hal.inc +ZIPLIST += $(SRCFOLDER)/$(HALFOLDER)/hal-c64-c128.inc +ZIPLIST += $(SRCFOLDER)/$(HALFOLDER)/hal-c16.inc +ZIPLIST += $(SRCFOLDER)/install.s +ZIPLIST += $(SRCFOLDER)/resident.s +ZIPLIST += $(SRCFOLDER)/customdrivecode.s +ZIPLIST += $(SRCFOLDER)/save.s + +ZIPLIST += $(INCFOLDER)/config.inc +ZIPLIST += $(INCFOLDER)/loader.inc + +BENCHMARKFOLDER = benchmark +CC65FOLDER = cc65 +DRIVECODEFOLDER = drivecode +MINEXAMPLEFOLDER = minexample +RESOURCESFOLDER = resources +SAVEFOLDER = save +STANDALONEFOLDER = standalone +TESTFOLDER = test +TURNDISKFOLDER = turndisk +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/benchmark.s +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/benchmark-bitfire.mon +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/benchmark-spindle.mon +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/make-tex-graph-bitfire.lua +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/make-tex-graph-spindle-code.lua +ZIPLIST += $(SAMPLESFOLDER)/$(BENCHMARKFOLDER)/make-tex-graph-spindle-graphics.lua +ZIPLIST += $(SAMPLESFOLDER)/$(CC65FOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(CC65FOLDER)/c-program.c +ZIPLIST += $(SAMPLESFOLDER)/$(CC65FOLDER)/Linkfile +ZIPLIST += $(SAMPLESFOLDER)/$(CC65FOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(DRIVECODEFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(DRIVECODEFOLDER)/drivecode.s +ZIPLIST += $(SAMPLESFOLDER)/$(DRIVECODEFOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(MINEXAMPLEFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(MINEXAMPLEFOLDER)/minexample.s +ZIPLIST += $(SAMPLESFOLDER)/$(MINEXAMPLEFOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/pic1.bin +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/pic2.bin +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/bootblock.bin +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/oxyron_oneder.prg +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/doom_c128.prg +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/threeve.prg +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/skew1.prg +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/test +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/a +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/b +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/c +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/d +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/e +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/f +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/g +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/h +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/i +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/j +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/k +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/l +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/m +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/n +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/o +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/p +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/q +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/r +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/x +ZIPLIST += $(SAMPLESFOLDER)/$(RESOURCESFOLDER)/y +ZIPLIST += $(SAMPLESFOLDER)/$(SAVEFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(SAVEFOLDER)/save.s +ZIPLIST += $(SAMPLESFOLDER)/$(SAVEFOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/Linkfile-c64 +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/Linkfile-c128 +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/Linkfile-c16 +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/standalone.s +ZIPLIST += $(SAMPLESFOLDER)/$(STANDALONEFOLDER)/loaderconfig.inc +ZIPLIST += $(SAMPLESFOLDER)/$(TESTFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(TESTFOLDER)/Linkfile +ZIPLIST += $(SAMPLESFOLDER)/$(TESTFOLDER)/test.s +ZIPLIST += $(SAMPLESFOLDER)/$(TURNDISKFOLDER)/Makefile +ZIPLIST += $(SAMPLESFOLDER)/$(TURNDISKFOLDER)/Linkfile +ZIPLIST += $(SAMPLESFOLDER)/$(TURNDISKFOLDER)/turndisk.s +ZIPLIST += $(SAMPLESFOLDER)/$(TURNDISKFOLDER)/loaderconfig.inc + +ZIPLIST += $(TOOLSFOLDER)/compressedfileconverter.pl + +CC1541FOLDER = cc1541 +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/LICENSE.txt +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/README.md +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/cc1541.c +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/cc1541.1.txt.in +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/cc1541.sln +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/cc1541.vcxproj +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/cc1541.vcxproj.filters +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/test_cc1541.c +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/test_cc1541.vcxproj +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/test_cc1541.vcxproj.filters +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/transwarp\ v0.84.prg +ZIPLIST += $(TOOLSFOLDER)/$(CC1541FOLDER)/transwarp\ v0.86.prg + +B2FOLDER = b2 +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/bb.c +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/bb.h +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/cruncher.c +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/cruncher.h +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/file.c +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/file.h +ZIPLIST += $(TOOLSFOLDER)/$(B2FOLDER)/Decruncher.inc + +ZIPLIST += $(TOOLSFOLDER)/bitnax-07a8c67/lz.c + +DOYNAXLZFOLDER = doynamite1.1 +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/readme.txt +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/decrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/lz.c +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/sfx.asm +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/test.asm +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/simple/decrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/simple/lz.c +ZIPLIST += $(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/krill/doynaxdecomp.s + +EXOFOLDER = exomizer-3.1 +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/changelog.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exo20info.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exo31info.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exobasic10b2.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/Makefile.test +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/Makefile.testP16 +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/6502emu.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/6502emu.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/areatrace.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/areatrace.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/asm.tab.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/asm.tab.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/asm.y +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/asm.yy +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/b2buf.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bas_main.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg_actions.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg_link_patch.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg_renumber.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg_trampoline.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/bprg.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/buf_io.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/buf_io.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/buf.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/buf.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/callback.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/chunkpool.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/chunkpool.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/common.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/desfx.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/desfx.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_helper.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_helper.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_main.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_raw.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_util.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exo_util.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exodec.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/exodec.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/expr.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/expr.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/flags.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/getflag.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/getflag.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/int.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/lex.yy.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/log.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/log.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/map.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/map.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/match.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/match.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/named_buffer.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/named_buffer.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/optimal.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/optimal.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/output.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/output.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/parse.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/parse.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/pc.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/pc.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/perf.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/perf.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/progress.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/progress.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/radix.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/radix.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/search.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/search.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/vec.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/vec.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/table.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/table.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/src/sfxdecr.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/README_exo3.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/c64.cfg +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/data.bin +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/exodecrunch.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/exostreamdecr1.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/exostreamdecr2.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/main.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/main1.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/main2.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/testrun.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/exodecrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/main.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/split/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/split/main.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/acme/split/data.asm.template +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/exodecrunch.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/main.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/split/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/split/main.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/dasm/split/data.s.template +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/perf/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/perf/data.raw +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/split/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/split/main.s +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/exodecrs/split/data.s.template +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/exodecr.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/exodecr.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/exodecrunch.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/exodecrunch.h +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/main.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/main2.c +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/test1.bin +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/test2.bin +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/test3.bin +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/test4.bin +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/6809/exo2_final.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/6809/exo2_puls.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/6809/README_exo3.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8080/P43/deexo.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8080/P43E/deexo.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8080/P47T4/deexo.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8080/README.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8086/P47/deexo.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/8086/README.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/thumb2/speed.S +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/thumb2/universal.S +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/thumb2/README.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_b0.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_b1.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_b2.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_b3.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_b4.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_f0.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_f1.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_f2.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_f3.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt_f4.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/deexoopt.asm +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/readme.txt +ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/rawdecrs/z80/lgpl-2.1.txt +#ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/win32/exobasic.exe +#ZIPLIST += $(TOOLSFOLDER)/$(EXOFOLDER)/win32/exomizer.exe + +LZSAFOLDER = lzsa +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/README.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/.gitignore +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/BlockFormat_LZSA1.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/BlockFormat_LZSA2.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/LICENSE +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/LICENSE.cc0.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/LICENSE.zlib.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/StreamFormat.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/pareto_graph.png +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/lzsa.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/matchfinder.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/matchfinder.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_block_v1.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_block_v1.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_block_v2.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_block_v2.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_block_v2_zx0_encoding.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_context.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_context.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_inmem.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_inmem.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_streaming.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/shrink_streaming.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/stream.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/stream.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/dictionary.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/dictionary.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_block_v1.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_block_v1.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_block_v2.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_block_v2.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_context.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_context.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_inmem.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_inmem.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_streaming.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/expand_streaming.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/format.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/frame.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/frame.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/lib.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/.gitignore +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CHANGELOG.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules/CheckLFS.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules/ProjectCPack.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/LICENSE +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/README.md +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/VERSION.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/bwt.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/mksary.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/sasearch.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/suftest.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/examples/unbwt.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/config.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/divsufsort.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/divsufsort.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/divsufsort_config.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/divsufsort_private.h +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/include/lfs.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/lib/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/lib/divsufsort.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/lib/divsufsort_utils.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/lib/sssort.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/lib/trsort.c +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/pkgconfig/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/VS2017/.gitignore +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/VS2017/lzsa.sln +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/VS2017/lzsa.vcxproj +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/VS2017/lzsa.vcxproj.filters +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/VS2017/lzsa.vcxproj.user +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/Xcode/lzsa.xcodeproj +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/Xcode/lzsa.xcodeproj/project.pbxproj +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502 +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_fast_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_fast_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_faster_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_faster_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_small_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6502/decompress_small_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/65816/decompress_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/65816/decompress_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa1-6309.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa1.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa1b-6309.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa1b.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa2-6309.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa2.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa2b-6309.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/6809/unlzsa2b.s +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/LZSA1FTA.ASM +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/LZSA1JMP.ASM +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/LZSA2FTA.ASM +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/decompress_small_v1.S +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/decompress_small_v2.S +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/decompress_speed_v1.S +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/8088/decompress_speed_v2.S +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/x86/decompress_small_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/x86/decompress_small_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/z80/unlzsa1_fast.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/z80/unlzsa1_small.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/z80/unlzsa2_fast.asm +ZIPLIST += $(TOOLSFOLDER)/$(LZSAFOLDER)/asm/z80/unlzsa2_small.asm + +NCFOLDER = nucrunch-1.0.1 +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/MANIFEST +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/Cargo.toml +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/readme.txt +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/decrunch.a65 +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/rdecrunch.a65 +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/boot.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/sboot.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/decrunch.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/rdecrunch.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/srdecrunch.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/src/boot.rs +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/src/lib.rs +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/src/main.rs +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/cbmcat +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/crc8.py +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/mkrings.py +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/gentest.py +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/testbed_ca65.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/testbed.a65 +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/rtestbed.a65 +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/sea_test.s +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/startaddr +ZIPLIST += $(TOOLSFOLDER)/$(NCFOLDER)/test/endaddr + +PUFOLDER = pucrunch +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/smakefile +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/pucrunch.c +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/pucrunch.h +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/uncrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/uncrunch-z80.asm +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/sa_uncrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(PUFOLDER)/cbmcombine.c + +SUBSIZERFOLDER = subsizer-0.7pre1 +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/README.txt +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/LICENSE.txt +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/bitfunc.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/bitfunc.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/bits-base.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/bits-base.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/buffer.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/buffer.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/crunch_normal.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/crunch_normal.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/global.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/global.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/histogram.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/histogram.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/match.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/match.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/memory.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/memory.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/message.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/message.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/params.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/params.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/pathfinder.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/pathfinder.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/subsizer.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/universal.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/universal.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/utils.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/utils.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/decrunch_normal_dirty.asm +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/decrunch_normal.asm +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/decrunchers_data.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/decrunchers.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/detect_start.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/detect_start.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/fold.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/fold.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/generate_sfx.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/generate_sfx.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/mach_c64.c +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/mach_c64.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src/sfx/ops6502.h +ZIPLIST += $(TOOLSFOLDER)/$(SUBSIZERFOLDER)/standalone/decrunch_normal.asm + +TCFOLDER = tinycrunch_v1.2 +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/readme.txt +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/bmp.bin +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/bmp.prg +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/tc_boot.prg +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/tc_boot.s +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/tc_decode.s +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/tc_decode_f.s +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/tc_encode.py +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/test/cbmcat +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/test/mkbmp.py +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/test/test.s +ZIPLIST += $(TOOLSFOLDER)/$(TCFOLDER)/test/testbin.s + +TSCRUNCHFOLDER = tscrunch +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/decrunch.asm +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/decrunch_extreme.asm +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/decrunch_small.asm +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/readme.txt +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/tscrunch.go +ZIPLIST += $(TOOLSFOLDER)/$(TSCRUNCHFOLDER)/tscrunch.py + +WCRUSHFOLDER = wcrush +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/LICENSE.TXT +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/README.TXT +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/decrush/decrush.tas +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/wca/main.cpp +#ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/wca/wca.exe +ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/wcrush/main.cpp +#ZIPLIST += $(TOOLSFOLDER)/$(WCRUSHFOLDER)/wcrush/wcrush.exe + +ZX0FOLDER = dali +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/dali.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/depack.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/dzx0_dali.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/dzx0_orig_zx0_v1.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/dzx0_orig_zx0_v2.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/sfx_fast.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/sfx_small.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/sfx.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/testfile.lz +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/Makefile +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/LICENSE +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/LICENSE.cc0.md +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/LICENSE.zlib.md +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/README.md +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/asm/6502/zx0_6502.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/asm/8088/unzx0_8088.S +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/asm/68000/unzx0_68000.S +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/asm/HuC6280/unpack-zx0.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/asm/Z80/unzx0v1_fast.asm +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/salvador.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/shrink.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/expand.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/matchfinder.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/matchfinder.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libsalvador.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/format.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/shrink.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/expand.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib/sssort.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib/trsort.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib/divsufsort.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib/divsufsort_utils.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/lib/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CHANGELOG.md +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/pkgconfig +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/pkgconfig/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules/ProjectCPack.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeModules/CheckLFS.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/README.md +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/suftest.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/unbwt.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/mksary.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/sasearch.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/examples/bwt.c +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/LICENSE +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/divsufsort_config.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/divsufsort_private.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/lfs.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/config.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/CMakeLists.txt +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/divsufsort.h.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/include/divsufsort.h +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/src/libdivsufsort/VERSION.cmake +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/VS2019 +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/VS2019/salvador.vcxproj.user +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/VS2019/salvador.vcxproj +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/VS2019/salvador.sln +ZIPLIST += $(TOOLSFOLDER)/$(ZX0FOLDER)/salvador/VS2019/salvador.vcxproj.filters + + +DISKIMAGES = $(BUILDFOLDER)/loadertest-c64.d64 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c64.d71 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c64.d81 +DISKIMAGES += $(BUILDFOLDER)/loader-c64.prg +DISKIMAGES += $(BUILDFOLDER)/install-c64.prg +DISKIMAGES += $(BUILDFOLDER)/loadersymbols-c64.inc + +DISKIMAGES += $(BUILDFOLDER)/loadertest-c128.d64 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c128.d71 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c128.d81 +DISKIMAGES += $(BUILDFOLDER)/loader-c128.prg +DISKIMAGES += $(BUILDFOLDER)/install-c128.prg +DISKIMAGES += $(BUILDFOLDER)/loadersymbols-c128.inc + +DISKIMAGES += $(BUILDFOLDER)/loadertest-c16.d64 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c16.d71 +DISKIMAGES += $(BUILDFOLDER)/loadertest-c16.d81 +DISKIMAGES += $(BUILDFOLDER)/loader-c16.prg +DISKIMAGES += $(BUILDFOLDER)/install-c16.prg +DISKIMAGES += $(BUILDFOLDER)/loadersymbols-c16.inc + +ZIPLIST += $(DISKIMAGES) + + +default: prg + +loader: + $(MAKE) -C src + +prg bin binary: + $(MAKE) -C src prg + +lib library: + $(MAKE) -C src lib + +prg-c64: + $(MAKE) -C src PLATFORM=c64 prg + +prg-c128: + $(MAKE) -C src PLATFORM=c128 prg + +prg-c16: + $(MAKE) -C src PLATFORM=c16 prg + +run: + $(CD) ..; $(MAKE) -C $(SAMPLESFOLDER)/$(TESTFOLDER) run + +$(BUILDFOLDER)/loadertest-c64.d64: + $(CD) ..; $(MAKE) -C $(SAMPLESFOLDER)/$(TESTFOLDER) all + +$(BUILDFOLDER)/loadertest-c128.d64: + $(CD) ..; $(MAKE) -C $(SAMPLESFOLDER)/$(TESTFOLDER) PLATFORM=c128 all + +$(BUILDFOLDER)/loadertest-c16.d64: + $(CD) ..; $(MAKE) -C $(SAMPLESFOLDER)/$(TESTFOLDER) PLATFORM=c16 all + +zip: prg-c64 prg-c128 prg-c16 $(BUILDFOLDER)/loadertest-c64.d64 $(BUILDFOLDER)/loadertest-c128.d64 $(BUILDFOLDER)/loadertest-c16.d64 version.inc + $(CD) ..; $(ZIP) $(ZIPFILE) $(ZIPLIST) + +loadertest: $(BUILDFOLDER)/loadertest-c64.d64 $(BUILDFOLDER)/loadertest-c128.d64 $(BUILDFOLDER)/loadertest-c16.d64 version.inc + $(CD) ..; $(ZIP_NO_PATHS) $(LOADERTEST) $(DISKIMAGES) + +.PHONY: tools cc1541 bitnax b2 doynax exomizer levelcrush lzsa2 nucrunch pucrunch subsizer zx0 +tools: cc1541 bitnax b2 doynax exomizer levelcrush lzsa2 nucrunch pucrunch subsizer zx0 + +bitnax: + $(CC) ../$(TOOLSFOLDER)/bitnax-07a8c67/lz.c -o tools/bitnax-07a8c67/lz + +b2: + $(MAKE) -C ../$(TOOLSFOLDER)/$(B2FOLDER) + +doynax: + $(CC) ../$(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/lz.c -o ../$(TOOLSFOLDER)/$(DOYNAXLZFOLDER)/lz + +exomizer: + $(MAKE) -C ../$(TOOLSFOLDER)/$(EXOFOLDER)/src + +levelcrush: + $(MAKE) -C ../$(TOOLSFOLDER)/$(WCRUSHFOLDER) + +lzsa2: + $(MAKE) -C ../$(TOOLSFOLDER)/$(LZSAFOLDER) + +nucrunch: + $(MAKE) -C ../$(TOOLSFOLDER)/$(NCFOLDER) + +pucrunch: + $(MAKE) -C ../$(TOOLSFOLDER)/$(PUFOLDER) + +subsizer: + $(MAKE) -C ../$(TOOLSFOLDER)/$(SUBSIZERFOLDER)/src + +zx0: + $(MAKE) -C ../$(TOOLSFOLDER)/$(ZX0FOLDER) + +cc1541: + $(MAKE) -C ../$(TOOLSFOLDER)/$(CC1541FOLDER) + +ifeq ($(NOWORKINGCOPY),0) +version.inc: ../.svn/entries + $(PRINTF) '.define REPOSITORY_VERSION ' > $@ + -$(PRINTF) '%d' $(VERSION) >> $@ + $(PRINTF) '\n' >> $@ + $(PRINTF) '.define REPOSITORY_VERSION_STRING "' >> $@ + $(PRINTF) '%s' $(VERSION) >> $@ + $(PRINTF) '"\n' >> $@ +endif diff --git a/loader/README b/loader/README new file mode 100644 index 0000000..b31642a --- /dev/null +++ b/loader/README @@ -0,0 +1,604 @@ +Krill's loader + +November 2022 +Krill@Plush.de + + +What it is +---------- +This is an easy-to-use arbitrarily interruptible flexible general-purpose +file-based disk loader system with optional decompression and limited +save-with-replace capability, aiming to provide maximum loading speed while +retaining full standard Commodore DOS format compatibility and also having a +reasonably small memory footprint. +Being fully standard DOS format compatible, disks and disk images may be +written by numerous available third-party copy, transfer, and disk image +manipulation programs. +The selection of byte-stream based compression algorithms includes many popular +crunchers. +Natively supported drives include all variants of the classic Commodore drive +families of 1541, 1570/71, 1581, as well as CMD FD drives, and 1541U. +Using the KERNAL fallback option, devices like SD2IEC (slow), IDE64 (fast), and +netload are supported non-natively. +Both PAL and NTSC video systems are supported. +The drive's CPU may run custom code provided by the user. + + +What it isn't +------------- +Simply put, anything that's not a read-only disk driver (with limited saving +capabilities). +Although any of the following features and functionality may be built on top of +it, it is not: +- a DOS: it cannot save to newly-created files, format disks, provide random + access below file level, or otherwise organise data on a disk +- a framework of any kind: it's only active when you tell it to, it does not do + anything in the background by itself, and the machine is fully yours when the + loader is idle +- a memory expansion server: files are not cached in REU, GeoRAM, RAMLink, + RAMDrive, or similar devices +- loading in an interrupt handler or another mainline thread: this can easily + and flexibly be implemented by the user + + +Features +-------- +- maximum raw loading speed of 7.7 kB/s (~20x) on 1541, typical speed is around + 7 kB/s (~18x) +- resident size: $01f9 bytes for combined load+depack using ZX0 (fits in + $0200..$03f8), $01fb bytes with TSCrunch (fits in $0200..$03fa), $e0 bytes + load-raw only +- IRQ/NMI/DMA/sprites/badlines are allowed without restrictions +- 2bit+ATN protocol (72 cycles per byte), multiple drives on the bus are + allowed, but loading only from the primary drive +- supported crunchers: Bitnax, Byteboozer2, Doynax-LZ, Exomizer, Levelcrush, + LZSA2, NuCrunch, Pucrunch, Subsizer, tinycrunch, TSCrunch, ZX0 +- depacking fully in-place using Bitnax, LZSA2, tinycrunch, TSCrunch or ZX0, + no clobbered bytes beyond unpacked data +- loading files in arbitrary order via directory with 0-terminated filename + strings +- loadnext functionality for sequential loading without specifying subsequent + filenames +- load address override for raw and crunched files +- PAL/NTSC support, NTSC is optional +- returns status codes for error checking and multiple disk handling +- optional loading and decompression to RAM at $d000..$dfff +- optional saving to pre-existing save-slot files +- optional memory decompression without loading +- optional loading via shadow directory for unrestricted dir-art +- custom drive code API facilitating co-processing on the drive's CPU +- full on-the-fly block GCR read+decode+checksumming +- native interleave at 100% CPU for loading uncompressed files is 3 revolutions + per track on tracks 18+, 4 on tracks 1-17, and 4 for all tracks for + compressed files +- implements Shrydar stepping for minimised seek time to an adjacent track +- free relocation of install routine, resident/transient portions and zeropage + variables + + +Folder structure overview +------------------------- +- loader this + - build output files + - docs documentation + - include include files + - src source code + - decompress decrunch routines + - drives drive-side code + - hal hardware abstraction layer implementations + - samples example projects + - benchmark benchmark using Bitfire/Sparkle corpus + - cc65 using the loader from a C program + - drivecode running custom code in the drive + - minexample minimum example + - resources graphics and other binaries + - save overwriting data in save-slot files + - standalone use loader from BASIC prompt + - test test application + - turndisk disk change handling + - tools utilities + - b2 Byteboozer2 + - bitnax-07a8c67 Bitnax + - cc1541 use cc1541 for optimised disk images + - dali ZX0 + - doynamite1.1 Doynax-LZ + - exomizer-3.1 Exomizer + - lzsa LZSA(2) + - nucrunch-1.0.1 NuCrunch + - pucrunch Pucrunch + - subsizer-0.7pre1 Subsizer + - tinycrunch_v1.2 tinycrunch + - tscrunch TSCrunch + - wcrush LevelCrush +- shared miscellaneous include files + + +Building the binaries +--------------------- +Prebuilt binaries (install-c64.prg, loader-c64.prg and loadersymbols-c64.inc) +can be found in loader/build. + +To build them, prerequisites are ca65 (see https://www.cc65.org, +https://cc65.github.io, or https://github.com/cc65/cc65), make, and perl. +In loader or loader/src, run + + $ make prg + +This will produce (and overwrite) install-c64.prg, loader-c64.prg, and +loadersymbols-c64.inc in loader/build. + +To specify zeropage, install and resident addresses, run, e.g., + + $ make prg ZP=e8 INSTALL=1000 RESIDENT=ce00 + +which will produce binaries using zeropage addresses $e8 and up, with the +install routine located at $1000 and the resident portion at $ce00. + +The two .prg files are regular C-64 program files with 2 bytes load address and +can be linked statically using .incbin or similar features of any 6502 +assembler/linker. +The symbols file is a text file defining the call addresses and zeropage +variables in canonical 6502 assembler syntax and is intended to be included from +assembly source code. + + +Configuration +------------- +The main configuration file is loader/include/config.inc. The individual +settings of the options are evaluated at build time and directly control various +aspects of the loader's functionality and its memory footprint. +This include file can be overridden by setting the EXTCONFIGPATH environment +variable to a folder containing a file named loaderconfig.inc. + +Note that NTSC support is optional, and disabled in the pre-built binaries. This +is because it slightly reduces PAL performance. However, resident portion +incarnations with and without NTSC support can be selected at run-time in order +to achieve maximum performance for both PAL and NTSC. + +Refer to loader/samples for various example projects using different individual +external configuration files. + + +Basic operation +--------------- +Before the loader can operate, its drive-side code portion(s) must be installed +in the drive(s). This is done by + + jsr install; subsequent loader calls will only work on the active drive as + ; denoted by FA = $ae + bcs error + ; loader installed successfully + +On error, the carry flag is set and an error code is returned in the accu. As +the install routine calls various KERNAL routines, the KERNAL ROM must be +enabled (e.g., $01 set to $36 or $37). +Note that the KERNAL routines also perform SEI and CLI, so it's advisable to set +up any custom interrupt handlers or enable sprites only after installing the +loader. +Also note that some variables in lowmem ($0200..$03ff) will be overwritten by +some KERNAL routines the install routine is calling. Thus, if the resident +portion resides in memory below $0400, make sure to copy it there only after +installing the loader. +Furthermore, in additon to FA = $ae, some more zeropage variables must have +valid values. See the bottom of loader/include/loader.inc for details. This list +may not be complete. +The drive-side portion remains resident in the drive. After successful +installation, the install routine is not needed any more and may be overwritten. +The KERNAL ROM may be disabled and zeropage variables clobbered. + +With the installed loader, files can only be loaded from the active drive, which +cannot be changed, and all other drives will remain inert: + + ldx #filename + jsr loadraw; load without decompression + bcs error; accu contains error code when carry is set after loading + ; unpacked file loaded successfully + + ldx #packed + jsr loadcompd; load with decompression + bcs error; accu contains error code when carry is set after loading + ; packed file loaded successfully + + [...] + + filename: + .petscii "unpacked" + .byte 0 + + packed: + .petscii "packed" + .byte 0 + +Note that the filename strings are 0-terminated. + +While loading using filenames with wildcards ("?" and "*") is not possible, +subsequent files following the previously-loaded file can be loaded via a +zero-length filename: + + ldx #nextfile + jsr loadraw; or loadcompd + bcs error + + [...] + + nextfile: + .byte 0 + +The first file to be loaded by the loader must be loaded via its actual +filename. Any subsequent files may then be loaded using the loadnext feature. + +It is strongly recommended to always check the carry flag for errors and handle +them. + +The loader code is completely inert when the loader is idle. Thus, the resident +code portion can be buffered away, overwritten, and copied back without any +problems. It is also possible to have different incarnations of the resident +code portion, such as with and without decompression, or with different +decompression routines. + +See loader/samples/minexample for a minimal loader usage example. + +Refer to loader/include/loader.inc for the definition of the error codes and +various ca65-syntax convenience macros illustrating usage of the various loader +calls. + + +Setting the VIC bank +-------------------- +The VIC bank may be set by writing to $dd00 at any time after installing the +loader, including setting it from an interrupt handler while loading. However, +the upper 6 bits must always be set to 0. This means that no masking of the +value read from $dd00 must be performed, and setting the VIC bank is as simple +as + + lda #vicbank; 4 legal values: $00, $01, $02 and $03 + sta $dd00 + +The 4 values are not inverted, i.e., $03 is the lowmost VIC bank at +$0000..$3fff, as usual. + +When $dd00 is buffered and later restored, the value written back must also be +restricted to 0..3. + + lda $dd00 + and #$03 + sta dd00buffer + + [...] + + lda dd00buffer + sta $dd00 + +Refer to loader/include/loader.inc for a ca65-syntax convenience macro to set +the VIC bank. + + +Zeropage usage +-------------- +The loader's zeropage variables may be overwritten while it's idle. While +loading, the addresses from loader_zp_first up to and including loader_zp_last +as denoted in loadersymbols-c64.inc must not be written to. + + +Handling disk changes +--------------------- +Waiting for a specific disk to be inserted is done by enabling the +FILE_EXISTS_API, then determining whether a specific disk is inserted by +checking in a loop whether a file that only exists on that disk is found. +The loop would look like this: + + retry: + ldx #filename + jsr fileexists + bcs retry; branch on file not found or error + ; file exists + +Once the file is found, it (or another file expected on the inserted disk) can +be loaded. + +Without the FILE_EXISTS_API, an alternative way to handle disk changes is to try +to load a file that only exists on that disk in a loop until the file (or its +first byte) is loaded successfully. +Thus, it is sufficient to perform + + retry: + ldx #filename + jsr loadraw; or loadcompd + bcs retry; branch on error + ; file loaded successfully + +The detection whether the file exists on the currently inserted disk can be sped +up by having the file be merely one block big, or by resetting loadaddrhi to 0 +before loading and then polling it for change from an interrupt handler. + +Refer to loader/samples/turndisk for an example. + + +Compressing files +----------------- +Generally, files need to be compressed (and decompressed) in forward (memory +address ascending) direction, and the compressed file's load address should be +set so that the end of the compressed data aligns with the end of its +uncompressed counterpart, such that in-place decompression can be performed. + +Refer to the Makefile in loader/ for the "tools" target to build all the +crunchers: + + $ make tools + +Also refer to loader/samples/benchmark/Makefile or loader/samples/test/Makefile +for the corresponding command line arguments for each of the supported crunchers. + + +Synchronisation +--------------- +For linearly loading and executing productions like demos, the general practice +is to tie events such as loading files (and subsequently executing them) to +specific music frame counts. +A music frame usually coincides with a video frame, as the player is called once +a frame, such that a frame count may be updated with calling the player. +As loading generally does not take a well-determined period of time to finish +(there is quite some deviation between different disks, same-model drives, and +also loads from the same disk using the same drive), it is a good practice to +sync to a minimum value of the frame count rather than an exact one: + + wait: + lda framecountlo + cmp nextloadlo + lda framecounthi + sbc nextloadhi + bcc wait; branch if framecount < nextload + ; load next file + +This way, given enough headroom between loads, after a delayed loading-finished +event, the next file would be loaded immediately, with the chain of events +eventually catching up with the desired synchronisation. + + +Dir-art +------- +Changing the amount of file blocks in the directory is strongly discouraged, as +this would disturb speculative loading and either slow down loading or cause +data to be loaded beyond the file's dedicated memory areas. + +However, there are a few ways to have an artful directory without interfering +with the loading scheme: + +Program files are recognised by the loader regardless of their apparent type, so +anything else than PRG is allowed, including deleted PRG files. + +For non-files, set their starting track to 0, such that they would not +needlessly occupy space in the drive-side directory buffer, and also for the +loadnext feature to work correctly. + +In order to only have only the first few characters of filenames be significant +for identification, set the FILENAME_MAXLENGTH option in the configuration file +to the amount of desired chars, e.g. 1, such that chars in the directory-listed +filenames beyond the first are ignored, and loading is effectively by names +like "1*", "A*", etc. + +For full freedom with the directory, including manipulating the amount of file +blocks listed, set DIRTRACK in the configuration file to anything else than 18 +(recommended is 17 or 19 to minimise seek times to and from the shadow directory +track), such that a shadow directory is used on that track by the loader. This +shadow directory would list the files normally, but be hidden and separate from +the normal directory listing, which can then be manipulated freely. + +Consider using cc1541 in loader/tools/cc1541 for creating disk images with a +shadow directory. + + +Optimising for speed +-------------------- +To get closer to the maximum speed, a few rules can be followed. These include +but are not limited to: + +The fewer and the bigger files are required to load a set of data, the faster +the data is loaded. Thus, it can be beneficial to pad gaps between loaded data, +or to re-arrange data, and merge files, minimising the number of files loaded. + +For better decrunch performance while loading, it may also be beneficial to +have the most compressible data at the beginning and progressively less +compressible data towards the end of a file. + +Generally, it's advisable to store sequentially-loaded files on ascending +tracks rather than alternatingly around the directory track 18. + +Loading uncompressed files, the loader's native interleave is 4 revolutions for +tracks 1-17, and 3 revolutions for tracks 18+. Thus, the highest raw throughput +is obtained on tracks 18-24, then 25-30, then 31+, and finally 1-17. + +Loading compressed files, the loader's native interleave is 4 revolutions +across the entire disk. Thus, the highest throughput is obtained on tracks 1-17 +then progressively decreasing with higher track numbers. + +The loader's speculative loading detects interleaves. However, the expected +block allocation does not follow the same rules as saving with the drive's +native DOS. + +This means that when adding the interleave on the current sector number in order +to obtain the next one, and there is a wrap (next sector >= number of sectors on +this track), then the new sector after wrap is a simple modulo on the number of +sectors the track can hold. I.e., + + sector = (sector + interleave) % number_of_sectors[track]; + +The DOS, instead, performs + + sector += interleave; + if (sector >= number_of_sectors[track]) { + sector -= number_of_sectors[track]; + if (sector > 0) { + --sector; + } + } + +While loading to the RAM under the I/O registers at $d000-$dfff is optionally +possible, it may slow down loading and increases the size of the resident +loader portion. Avoiding this option and working around loading to $d000-$dfff +by copying after loading or decompressing to that range only after loading may +speed up general loader operation. + +Consider using cc1541 in loader/tools/cc1541 for creating disk images with +optimised layouts. + + +Loading in the background +------------------------- +The loader normally loads in the mainline thread. If it shall load within a +periodically-executed interrupt handler, i.e., in another mainline thread, the +context must be switched accordingly. That is, stack pointer, registers and +condition flags must be buffered when switching from the current thread, and the +context of the thread being switched to must be restored. This can be +implemented on top of the loader and is orthogonal to its functionality. + + +Saving files +------------ +To build the loader with file-save functionality, run, e.g., + + $ make save ZP=e8 INSTALL=1000 RESIDENT=cd00 TRANSIENT=2000 + +in loader or loader/src. + +Additionally to install-c64.prg, loader-c64.prg, and loadersymbols-c64.inc, this +will produce save-c64.prg in loader/build, assembled to the TRANSIENT memory +region. + +Files can be saved like so: + + ldx #saveparams + jsr save + bcs error + ; file was saved successfully + + [...] + + saveparams: + .word filename + .word $2000; from + .word $0123; length in bytes + .word $2000; loadaddress + .word $8000; drive-code buffer, $0800 bytes to scratch + + filename: + .petscii "hiscores" + .byte 0 + +The file to save must exist on disk prior to saving. I.e., it is overwritten +in its entirety, replacing the original file. + +Note that save-c64.prg need not be resident in computer memory, and can be +loaded, used and then overwritten again. + +Refer to loader/samples/save for an example. + + +Limitations +----------- +Internally, the loader's drive-side directory buffer stores 16-bit hash values +of filenames. There is a certain chance of collision, such that two files with +different filenames may have the same filename hash and thus appear as the same +file to the loader, and it would "randomly" load either file without reporting +an error. Using cc1541 in loader/tools/cc1541, hash collisions are detected and +can then be worked around by renaming files or changing the loader's +FILENAME_MAXLENGTH configuration setting, which must be reflected with cc1541's +-M option. + +There must always be at least 2 file entries in the directory. + +The final directory block must begin with 00 ff. Some custom disk image creation +tools produce an erroneous final directory block, such that its first bytes are +00 00. This is an invalid block length, and the loader rejects blocks with +invalid track/sector links or block sizes. In the case of an invalid directory +block, the loader keeps retrying to read it, effectively locking up. + +As mentioned in the dir-art section, the number of file blocks listed in the +directory must not be changed, unless having a shadow directory (with the actual +number of file blocks). + +If the loader is given extremely little raster time for loading, or even starved +for multiple video frames, it's adviseable to enable the DISABLE_WATCHDOG option +in loader/include/loader.inc. This will disengage the watchdog interrupt in the +drive-side code portion, which is otherwise triggering on protocol breaches and +would reset the drive on unexpected errors, such as the computer crashing or +resetting during loading, or cartridge freeze. The rationale is that the drive +would become accessible normally without the need to power-cycle or manually +reset it. + +If not using Bitnax, LZSA2, tinycrunch, TSCrunch or ZX0, compressed files will +usually go a few bytes beyond their corresponding uncompressed data. This means +that some bytes directly after the uncompressed files' range in memory are +overwritten. This can be worked around by not using those areas while loading. + +However, when crunched files are larger than their uncompressed counterparts, +data before the uncompressed memory region may be overwritten. To work around +this, add some padding at the end of the uncompressed files. + + +License +------- +TL;DR: Please give credit, thanks. + +When using this software or a derivative thereof in own works, please give +appropriate credit, e.g., "Loader by Krill". +This does not need to be on prominent display within the actual running +production itself, but at least in an accompanying note, readme, info file, +directory, or a similar means of attribution contained in the official release +archive or disk images. +Exclusively external credits on websites or online forums are no such instances. + +Copyright 2022 Gunnar Ruthenberg + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice in +an appropriate form, e.g., "Loader by Krill" displayed anywhere in the actual +production itself and/or accompanying documentation contained in the official +release archive or disk images. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Third-party software (particularly in loader/tools and loader/samples/resources) +and its derivatives (particularly in loader/src/decompress) are not affected and +retain their original licenses. + + +Thanks +------ +For extensive beta-testing, i thank, in alphabetical order: + +Adder, Algorithm, ChristopherJam, Dr. Science, Jojeli, Mr Ammo, Nomistake, +Perplex, q0w, root42, willymanilly. + +For some extra-extensive testing effort, i thank, again: + +q0w and Dr. Science. + +Special thanks to Bitbreaker for coming up with the partial overlap GCR decoding +trick that has made the 124-cycle read loop possible, and to ChristopherJam aka +Shrydar, again, for some valuable ideas and input, and the concentric circles +bitmap. diff --git a/loader/build/install-c128.prg b/loader/build/install-c128.prg new file mode 100644 index 0000000..63c1f6c Binary files /dev/null and b/loader/build/install-c128.prg differ diff --git a/loader/build/install-c16.prg b/loader/build/install-c16.prg new file mode 100644 index 0000000..98beed2 Binary files /dev/null and b/loader/build/install-c16.prg differ diff --git a/loader/build/install-c64.prg b/loader/build/install-c64.prg new file mode 100644 index 0000000..c601a31 Binary files /dev/null and b/loader/build/install-c64.prg differ diff --git a/loader/build/intermediate/binary.link b/loader/build/intermediate/binary.link new file mode 100644 index 0000000..8847adf --- /dev/null +++ b/loader/build/intermediate/binary.link @@ -0,0 +1,17 @@ +MEMORY +{ + ZPRAM: start = $2, size = $fe; + ZPRAM2: start = $2, size = $fe; + INSTALLRAM: start = $17fe, size = $e802, file = "../build/install-c64.prg"; + RESIDENTRAM: start = $16fe, size = $e902, file = "../build/loader-c64.prg"; + TRANSIENTRAM: start = $3ffe, size = $c002, file = "../build/transient-c64.prg"; +} + +SEGMENTS +{ + DISKIO_ZP: load = ZPRAM, type = zp; + DISKIO_PLUGIN_ZP: load = ZPRAM2, type = zp, optional = yes; + DISKIO: load = RESIDENTRAM; + DISKIO_PLUGIN: load = TRANSIENTRAM, optional = yes; + DISKIO_INSTALL: load = INSTALLRAM; +} diff --git a/loader/build/intermediate/install-nonreloc-c64.o b/loader/build/intermediate/install-nonreloc-c64.o new file mode 100644 index 0000000..9e41f3b Binary files /dev/null and b/loader/build/intermediate/install-nonreloc-c64.o differ diff --git a/loader/build/intermediate/loader-c64.map b/loader/build/intermediate/loader-c64.map new file mode 100644 index 0000000..1c7b4dd --- /dev/null +++ b/loader/build/intermediate/loader-c64.map @@ -0,0 +1,267 @@ +Modules list: +------------- +loader-nonreloc-c64.o: + CODE Offs=000000 Size=000000 Align=00001 Fill=0000 + RODATA Offs=000000 Size=000000 Align=00001 Fill=0000 + BSS Offs=000000 Size=000000 Align=00001 Fill=0000 + DATA Offs=000000 Size=000000 Align=00001 Fill=0000 + ZEROPAGE Offs=000000 Size=000000 Align=00001 Fill=0000 + NULL Offs=000000 Size=000000 Align=00001 Fill=0000 + DISKIO_ZP Offs=000000 Size=000005 Align=00001 Fill=0000 + DISKIO Offs=000000 Size=0000E2 Align=00001 Fill=0000 +install-nonreloc-c64.o: + CODE Offs=000000 Size=000000 Align=00001 Fill=0000 + RODATA Offs=000000 Size=000000 Align=00001 Fill=0000 + BSS Offs=000000 Size=000000 Align=00001 Fill=0000 + DATA Offs=000000 Size=000000 Align=00001 Fill=0000 + ZEROPAGE Offs=000000 Size=000000 Align=00001 Fill=0000 + NULL Offs=000000 Size=000000 Align=00001 Fill=0000 + DISKIO_INSTALL Offs=000000 Size=001B55 Align=00001 Fill=0000 + + +Segment list: +------------- +Name Start End Size Align +---------------------------------------------------- +BSS 000000 000000 000000 00001 +CODE 000000 000000 000000 00001 +DATA 000000 000000 000000 00001 +NULL 000000 000000 000000 00001 +RODATA 000000 000000 000000 00001 +ZEROPAGE 000000 000000 000000 00001 +DISKIO_ZP 000002 000006 000005 00001 +DISKIO 0016FE 0017DF 0000E2 00001 +DISKIO_INSTALL 0017FE 003352 001B55 00001 + + +Exports list by name: +--------------------- +BLOCKBUFFER41 000100 EA BLOCKBUFFER71 000700 EA +BLOCKBUFFER81 000B00 EA BLOCKDESTLO 000004 LZ +CLEARSECTORLINKTABLE71 00000A EZ CURRTRACK41 00007A EZ +CURRTRACK71 000000 EZ CUSTOMPARAM81 000017 EZ +CUSTOMRECEIVE81 001FD7 EA CUSTOMUPLOADSIZE71 0000DD EZ +CUSTOMZPBUFFER71 000037 EZ DECOMPRESSOR_NONE 000000 EZ +DECOMPVARS 000007 LZ DIRBUFFSIZE71 000013 EZ +DIRBUFFSIZE81 000058 EZ DIRSECTORS71 000074 EZ +DIRTRACKS81 0007A0 EA DRVCODEND81 0007A0 EA +FILENAME41 0000EF EZ FILENAME71 000037 EZ +FILESECTOR41 000076 EZ GETBYTE_CLOCK_ATN_HI 000006 LZ +ID041 000059 EZ LEDSTATE41 0000ED EZ +LEDSTATE71 000002 EZ LINKSECTOR41 00007F EZ +LINKSECTOR71 000008 EZ LINKTRACK41 0000B8 EZ +LINKTRACK71 00000D EZ NUMFILES41 000078 EZ +REQUESTEDSECTOR41 0000EE EZ SECTORLINKTABLE41 00003E EZ +SECTORLINKTABLE71 00001F EZ V1B41 000007 EZ +V2B41 00000F EZ bsetv2b71 0000F6 LZ +bsyledon81 000429 LA cmdfdfix0 0005B3 RLA +cmdfdfix1 000373 REA cmdfdfix2 000374 REA +cmdfdfix3 000702 REA cmdfdfix4 000704 REA +config_ALLOW_2_MHZ_ON_C128 000000 EZ config_DECOMPRESSOR 000000 EZ +config_DIRTRACK 000012 EZ config_DIRTRACK81 000028 EZ +config_END_ADDRESS_API 000000 EZ config_FILENAME_MAXLENGTH 000010 EZ +config_FILE_EXISTS_API 000000 EZ config_INTERNAL 000000 EZ +config_LOAD_COMPD_API 000000 EZ config_LOAD_RAW_API 000001 EZ +config_LOAD_TO_API 000000 EZ config_LOAD_UNDER_D000_DFFF 000000 EZ +config_LOAD_VIA_KERNAL_FALLBACK 000000 EZ config_MEM_DECOMP_API 000000 EZ +config_MEM_DECOMP_TO_API 000000 EZ config_NTSC_COMPATIBILITY 000000 EZ +config_ONLY_1541_AND_COMPATIBLE 000000 EZ config_PREFER_SPEED_OVER_SIZE 000000 EZ +config_UNINSTALL_API 000000 EZ dcodinit81 00074C LA +enablwdg81 0003AF LA filename81 00031B LA +findfile41 000523 EA findfile71 000432 EA +findfile81 0004D4 EA getblock41 000381 LA +getblock71 0002CF LA getblock81 0003CD LA +getbyte41 0004FD EA getbytecmp41 00050A LA +getbyterts41 00050F EA getbytewdog41 0004F4 EA +idleloop41 0004E3 LA idleloop71 0003FC LA +idxloop41 000424 LA idxloop71 00065C LA +initcntr81 0003F3 LA initlink41 00027B LA +initlink71 000192 LA initwdog81 000397 LA +install 001800 LA loadaddrhi 000003 LZ +loadaddrlo 000002 RLZ loader_zp_first 000002 EZ +loader_zp_last 000006 EZ loadfile41 0005BA LA +loadfile71 0004E1 LA loadfile81 000596 LA +loadraw 001700 LA onemhz71 0000DF LZ +sertoraw41 000216 LA setbv2b41 0002AD LA +status_DEVICE_INCOMPATIBLE 0000FB EZ status_DEVICE_NOT_PRESENT 0000FE EZ +status_FILE_NOT_FOUND 0000FF EZ status_GENERIC_KERNAL_ERROR 0000FD EZ +status_OK 000000 EZ status_TOO_MANY_DEVICES 0000FC EZ +swapzp81 000343 LA topofstack41 000106 EA +topofstack71 0001D4 EA trkseek41 00022C LA +trkseek71 000111 LA wdogentr41 0004C4 LA + + + +Exports list by value: +---------------------- +config_END_ADDRESS_API 000000 EZ CURRTRACK71 000000 EZ +DECOMPRESSOR_NONE 000000 EZ config_UNINSTALL_API 000000 EZ +config_PREFER_SPEED_OVER_SIZE 000000 EZ config_ONLY_1541_AND_COMPATIBLE 000000 EZ +config_NTSC_COMPATIBILITY 000000 EZ config_MEM_DECOMP_TO_API 000000 EZ +config_MEM_DECOMP_API 000000 EZ config_LOAD_VIA_KERNAL_FALLBACK 000000 EZ +config_LOAD_UNDER_D000_DFFF 000000 EZ status_OK 000000 EZ +config_LOAD_TO_API 000000 EZ config_LOAD_COMPD_API 000000 EZ +config_INTERNAL 000000 EZ config_FILE_EXISTS_API 000000 EZ +config_ALLOW_2_MHZ_ON_C128 000000 EZ config_DECOMPRESSOR 000000 EZ +config_LOAD_RAW_API 000001 EZ loader_zp_first 000002 EZ +loadaddrlo 000002 RLZ LEDSTATE71 000002 EZ +loadaddrhi 000003 LZ BLOCKDESTLO 000004 LZ +loader_zp_last 000006 EZ GETBYTE_CLOCK_ATN_HI 000006 LZ +V1B41 000007 EZ DECOMPVARS 000007 LZ +LINKSECTOR71 000008 EZ CLEARSECTORLINKTABLE71 00000A EZ +LINKTRACK71 00000D EZ V2B41 00000F EZ +config_FILENAME_MAXLENGTH 000010 EZ config_DIRTRACK 000012 EZ +DIRBUFFSIZE71 000013 EZ CUSTOMPARAM81 000017 EZ +SECTORLINKTABLE71 00001F EZ config_DIRTRACK81 000028 EZ +CUSTOMZPBUFFER71 000037 EZ FILENAME71 000037 EZ +SECTORLINKTABLE41 00003E EZ DIRBUFFSIZE81 000058 EZ +ID041 000059 EZ DIRSECTORS71 000074 EZ +FILESECTOR41 000076 EZ NUMFILES41 000078 EZ +CURRTRACK41 00007A EZ LINKSECTOR41 00007F EZ +LINKTRACK41 0000B8 EZ CUSTOMUPLOADSIZE71 0000DD EZ +onemhz71 0000DF LZ LEDSTATE41 0000ED EZ +REQUESTEDSECTOR41 0000EE EZ FILENAME41 0000EF EZ +bsetv2b71 0000F6 LZ status_DEVICE_INCOMPATIBLE 0000FB EZ +status_TOO_MANY_DEVICES 0000FC EZ status_GENERIC_KERNAL_ERROR 0000FD EZ +status_DEVICE_NOT_PRESENT 0000FE EZ status_FILE_NOT_FOUND 0000FF EZ +BLOCKBUFFER41 000100 EA topofstack41 000106 EA +trkseek71 000111 LA initlink71 000192 LA +topofstack71 0001D4 EA sertoraw41 000216 LA +trkseek41 00022C LA initlink41 00027B LA +setbv2b41 0002AD LA getblock71 0002CF LA +filename81 00031B LA swapzp81 000343 LA +cmdfdfix1 000373 REA cmdfdfix2 000374 REA +getblock41 000381 LA initwdog81 000397 LA +enablwdg81 0003AF LA getblock81 0003CD LA +initcntr81 0003F3 LA idleloop71 0003FC LA +idxloop41 000424 LA bsyledon81 000429 LA +findfile71 000432 EA wdogentr41 0004C4 LA +findfile81 0004D4 EA loadfile71 0004E1 LA +idleloop41 0004E3 LA getbytewdog41 0004F4 EA +getbyte41 0004FD EA getbytecmp41 00050A LA +getbyterts41 00050F EA findfile41 000523 EA +loadfile81 000596 LA cmdfdfix0 0005B3 RLA +loadfile41 0005BA LA idxloop71 00065C LA +BLOCKBUFFER71 000700 EA cmdfdfix3 000702 REA +cmdfdfix4 000704 REA dcodinit81 00074C LA +DRVCODEND81 0007A0 EA DIRTRACKS81 0007A0 EA +BLOCKBUFFER81 000B00 EA loadraw 001700 LA +install 001800 LA CUSTOMRECEIVE81 001FD7 EA + + + +Imports list: +------------- +BLOCKBUFFER41 (install-nonreloc-c64.o): +BLOCKBUFFER71 (install-nonreloc-c64.o): +BLOCKBUFFER81 (install-nonreloc-c64.o): +BLOCKDESTLO (loader-nonreloc-c64.o): +CLEARSECTORLINKTABLE71 (install-nonreloc-c64.o): +CURRTRACK41 (install-nonreloc-c64.o): +CURRTRACK71 (install-nonreloc-c64.o): +CUSTOMPARAM81 (install-nonreloc-c64.o): +CUSTOMRECEIVE81 (install-nonreloc-c64.o): +CUSTOMUPLOADSIZE71 (install-nonreloc-c64.o): +CUSTOMZPBUFFER71 (install-nonreloc-c64.o): +DECOMPRESSOR_NONE (loader-nonreloc-c64.o): +DECOMPVARS (loader-nonreloc-c64.o): +DIRBUFFSIZE71 (install-nonreloc-c64.o): +DIRBUFFSIZE81 (install-nonreloc-c64.o): +DIRSECTORS71 (install-nonreloc-c64.o): +DIRTRACKS81 (install-nonreloc-c64.o): +DRVCODEND81 (install-nonreloc-c64.o): +FILENAME41 (install-nonreloc-c64.o): +FILENAME71 (install-nonreloc-c64.o): +FILESECTOR41 (install-nonreloc-c64.o): +GETBYTE_CLOCK_ATN_HI (loader-nonreloc-c64.o): +ID041 (install-nonreloc-c64.o): +LEDSTATE41 (install-nonreloc-c64.o): +LEDSTATE71 (install-nonreloc-c64.o): +LINKSECTOR41 (install-nonreloc-c64.o): +LINKSECTOR71 (install-nonreloc-c64.o): +LINKTRACK41 (install-nonreloc-c64.o): +LINKTRACK71 (install-nonreloc-c64.o): +NUMFILES41 (install-nonreloc-c64.o): +REQUESTEDSECTOR41 (install-nonreloc-c64.o): +SECTORLINKTABLE41 (install-nonreloc-c64.o): +SECTORLINKTABLE71 (install-nonreloc-c64.o): +V1B41 (install-nonreloc-c64.o): +V2B41 (install-nonreloc-c64.o): +bsetv2b71 (install-nonreloc-c64.o): +bsyledon81 (install-nonreloc-c64.o): +cmdfdfix0 (install-nonreloc-c64.o): + install-nonreloc-c64.o install.s(29) +cmdfdfix1 (install-nonreloc-c64.o): + install-nonreloc-c64.o install.s(30) +cmdfdfix2 (install-nonreloc-c64.o): + install-nonreloc-c64.o install.s(31) +cmdfdfix3 (install-nonreloc-c64.o): + install-nonreloc-c64.o install.s(33) +cmdfdfix4 (install-nonreloc-c64.o): + install-nonreloc-c64.o install.s(34) +config_ALLOW_2_MHZ_ON_C128 (install-nonreloc-c64.o): +config_DECOMPRESSOR (install-nonreloc-c64.o): +config_DIRTRACK (install-nonreloc-c64.o): +config_DIRTRACK81 (install-nonreloc-c64.o): +config_END_ADDRESS_API (install-nonreloc-c64.o): +config_FILENAME_MAXLENGTH (install-nonreloc-c64.o): +config_FILE_EXISTS_API (install-nonreloc-c64.o): +config_INTERNAL (install-nonreloc-c64.o): +config_LOAD_COMPD_API (install-nonreloc-c64.o): +config_LOAD_RAW_API (install-nonreloc-c64.o): +config_LOAD_TO_API (install-nonreloc-c64.o): +config_LOAD_UNDER_D000_DFFF (install-nonreloc-c64.o): +config_LOAD_VIA_KERNAL_FALLBACK (install-nonreloc-c64.o): +config_MEM_DECOMP_API (install-nonreloc-c64.o): +config_MEM_DECOMP_TO_API (install-nonreloc-c64.o): +config_NTSC_COMPATIBILITY (install-nonreloc-c64.o): +config_ONLY_1541_AND_COMPATIBLE (install-nonreloc-c64.o): +config_PREFER_SPEED_OVER_SIZE (install-nonreloc-c64.o): +config_UNINSTALL_API (install-nonreloc-c64.o): +dcodinit81 (install-nonreloc-c64.o): +enablwdg81 (install-nonreloc-c64.o): +filename81 (install-nonreloc-c64.o): +findfile41 (install-nonreloc-c64.o): +findfile71 (install-nonreloc-c64.o): +findfile81 (install-nonreloc-c64.o): +getblock41 (install-nonreloc-c64.o): +getblock71 (install-nonreloc-c64.o): +getblock81 (install-nonreloc-c64.o): +getbyte41 (install-nonreloc-c64.o): +getbytecmp41 (install-nonreloc-c64.o): +getbyterts41 (install-nonreloc-c64.o): +getbytewdog41 (install-nonreloc-c64.o): +idleloop41 (install-nonreloc-c64.o): +idleloop71 (install-nonreloc-c64.o): +idxloop41 (install-nonreloc-c64.o): +idxloop71 (install-nonreloc-c64.o): +initcntr81 (install-nonreloc-c64.o): +initlink41 (install-nonreloc-c64.o): +initlink71 (install-nonreloc-c64.o): +initwdog81 (install-nonreloc-c64.o): +install (install-nonreloc-c64.o): +loadaddrhi (loader-nonreloc-c64.o): +loadaddrlo (loader-nonreloc-c64.o): + install-nonreloc-c64.o ./../include/loader.inc(441) +loader_zp_first (loader-nonreloc-c64.o): +loader_zp_last (loader-nonreloc-c64.o): +loadfile41 (install-nonreloc-c64.o): +loadfile71 (install-nonreloc-c64.o): +loadfile81 (install-nonreloc-c64.o): +loadraw (loader-nonreloc-c64.o): +onemhz71 (install-nonreloc-c64.o): +sertoraw41 (install-nonreloc-c64.o): +setbv2b41 (install-nonreloc-c64.o): +status_DEVICE_INCOMPATIBLE (install-nonreloc-c64.o): +status_DEVICE_NOT_PRESENT (install-nonreloc-c64.o): +status_FILE_NOT_FOUND (install-nonreloc-c64.o): +status_GENERIC_KERNAL_ERROR (install-nonreloc-c64.o): +status_OK (install-nonreloc-c64.o): +status_TOO_MANY_DEVICES (install-nonreloc-c64.o): +swapzp81 (install-nonreloc-c64.o): +topofstack41 (install-nonreloc-c64.o): +topofstack71 (install-nonreloc-c64.o): +trkseek41 (install-nonreloc-c64.o): +trkseek71 (install-nonreloc-c64.o): +wdogentr41 (install-nonreloc-c64.o): + diff --git a/loader/build/intermediate/loader-nonreloc-c64.o b/loader/build/intermediate/loader-nonreloc-c64.o new file mode 100644 index 0000000..d0ce8f5 Binary files /dev/null and b/loader/build/intermediate/loader-nonreloc-c64.o differ diff --git a/loader/build/intermediate/minexample-uncompressed-c64.prg b/loader/build/intermediate/minexample-uncompressed-c64.prg new file mode 100644 index 0000000..22e7a1f Binary files /dev/null and b/loader/build/intermediate/minexample-uncompressed-c64.prg differ diff --git a/loader/build/loader-c64.prg b/loader/build/loader-c64.prg new file mode 100644 index 0000000..09b5a55 Binary files /dev/null and b/loader/build/loader-c64.prg differ diff --git a/loader/build/loadersymbols-c128.inc b/loader/build/loadersymbols-c128.inc new file mode 100644 index 0000000..41afa3c --- /dev/null +++ b/loader/build/loadersymbols-c128.inc @@ -0,0 +1,44 @@ +; repository version 194, built on Thu, 24 Nov 2022 10:56:24 +0100 for project "loader" using +; make PLATFORM=c128 prg INSTALL=1000 RESIDENT=0200 ZP=e0 PROJECT= + +; configuration +config_ALLOW_2_MHZ_ON_C128 = 0 +config_DECOMPRESSOR = 12; ZX0 +config_DIRTRACK = 18 +config_DIRTRACK81 = 40 +config_END_ADDRESS_API = 0 +config_FILENAME_MAXLENGTH = 16 +config_FILE_EXISTS_API = 0 +config_INTERNAL = 0 +config_LOAD_COMPD_API = 1 +config_LOAD_RAW_API = 1 +config_LOAD_TO_API = 0 +config_LOAD_UNDER_D000_DFFF = 0 +config_LOAD_VIA_KERNAL_FALLBACK = 0 +config_MEM_DECOMP_API = 0 +config_MEM_DECOMP_TO_API = 0 +config_NTSC_COMPATIBILITY = 0 +config_ONLY_1541_AND_COMPATIBLE = 0 +config_PREFER_SPEED_OVER_SIZE = 0 +config_UNINSTALL_API = 0 + +; status codes +status_OK = $00 +status_DEVICE_INCOMPATIBLE = $fb +status_TOO_MANY_DEVICES = $fc +status_GENERIC_KERNAL_ERROR = $fd +status_DEVICE_NOT_PRESENT = $fe +status_FILE_NOT_FOUND = $ff + +; zeropage $e0-$f0 +loader_zp_first = $e0 +loadaddrlo = $e0 +loadaddrhi = $e1 +loader_zp_last = $ef + +; install $1000-$2c38 +install = $1000 + +; resident $0200-$0472 +loadraw = $0200 +loadcompd = $020e diff --git a/loader/build/loadersymbols-c16.inc b/loader/build/loadersymbols-c16.inc new file mode 100644 index 0000000..ba47c6d --- /dev/null +++ b/loader/build/loadersymbols-c16.inc @@ -0,0 +1,43 @@ +; repository version 194, built on Thu, 24 Nov 2022 10:56:24 +0100 for project "loader" using +; make PLATFORM=c16 prg INSTALL=1000 RESIDENT=0200 ZP=e0 PROJECT= + +; configuration +config_DECOMPRESSOR = 12; ZX0 +config_DIRTRACK = 18 +config_DIRTRACK81 = 40 +config_END_ADDRESS_API = 0 +config_FILENAME_MAXLENGTH = 16 +config_FILE_EXISTS_API = 0 +config_INTERNAL = 0 +config_LOAD_COMPD_API = 1 +config_LOAD_RAW_API = 1 +config_LOAD_TO_API = 0 +config_LOAD_UNDER_D000_DFFF = 0 +config_LOAD_VIA_KERNAL_FALLBACK = 0 +config_MEM_DECOMP_API = 0 +config_MEM_DECOMP_TO_API = 0 +config_NTSC_COMPATIBILITY = 0 +config_ONLY_1541_AND_COMPATIBLE = 0 +config_PREFER_SPEED_OVER_SIZE = 0 +config_UNINSTALL_API = 0 + +; status codes +status_OK = $00 +status_DEVICE_INCOMPATIBLE = $fb +status_TOO_MANY_DEVICES = $fc +status_GENERIC_KERNAL_ERROR = $fd +status_DEVICE_NOT_PRESENT = $fe +status_FILE_NOT_FOUND = $ff + +; zeropage $e0-$ef +loader_zp_first = $e0 +loadaddrlo = $e0 +loadaddrhi = $e1 +loader_zp_last = $ee + +; install $1000-$2b93 +install = $1000 + +; resident $0200-$0464 +loadraw = $0200 +loadcompd = $020e diff --git a/loader/build/loadersymbols-c64.inc b/loader/build/loadersymbols-c64.inc new file mode 100644 index 0000000..c15ab83 --- /dev/null +++ b/loader/build/loadersymbols-c64.inc @@ -0,0 +1,43 @@ +; repository version 194, built on Wed, 12 Nov 2025 15:49:37 +0300 for project "loader" using +; make PLATFORM=c64 prg INSTALL=1800 RESIDENT=1700 ZP=02 PROJECT= + +; configuration +config_ALLOW_2_MHZ_ON_C128 = 0 +config_DECOMPRESSOR = 0; NONE +config_DIRTRACK = 18 +config_DIRTRACK81 = 40 +config_END_ADDRESS_API = 0 +config_FILENAME_MAXLENGTH = 16 +config_FILE_EXISTS_API = 0 +config_INTERNAL = 0 +config_LOAD_COMPD_API = 0 +config_LOAD_RAW_API = 1 +config_LOAD_TO_API = 0 +config_LOAD_UNDER_D000_DFFF = 0 +config_LOAD_VIA_KERNAL_FALLBACK = 0 +config_MEM_DECOMP_API = 0 +config_MEM_DECOMP_TO_API = 0 +config_NTSC_COMPATIBILITY = 0 +config_ONLY_1541_AND_COMPATIBLE = 0 +config_PREFER_SPEED_OVER_SIZE = 0 +config_UNINSTALL_API = 0 + +; status codes +status_OK = $00 +status_DEVICE_INCOMPATIBLE = $fb +status_TOO_MANY_DEVICES = $fc +status_GENERIC_KERNAL_ERROR = $fd +status_DEVICE_NOT_PRESENT = $fe +status_FILE_NOT_FOUND = $ff + +; zeropage $02-$07 +loader_zp_first = $02 +loadaddrlo = $02 +loadaddrhi = $03 +loader_zp_last = $06 + +; install $1800-$3353 +install = $1800 + +; resident $1700-$17e0 +loadraw = $1700 diff --git a/loader/build/loadertest-c128.d64 b/loader/build/loadertest-c128.d64 new file mode 100644 index 0000000..0028eaf Binary files /dev/null and b/loader/build/loadertest-c128.d64 differ diff --git a/loader/build/loadertest-c128.d71 b/loader/build/loadertest-c128.d71 new file mode 100644 index 0000000..ec376a4 Binary files /dev/null and b/loader/build/loadertest-c128.d71 differ diff --git a/loader/build/loadertest-c128.d81 b/loader/build/loadertest-c128.d81 new file mode 100644 index 0000000..1e2abe2 Binary files /dev/null and b/loader/build/loadertest-c128.d81 differ diff --git a/loader/build/loadertest-c16.d64 b/loader/build/loadertest-c16.d64 new file mode 100644 index 0000000..9819fcf Binary files /dev/null and b/loader/build/loadertest-c16.d64 differ diff --git a/loader/build/loadertest-c16.d71 b/loader/build/loadertest-c16.d71 new file mode 100644 index 0000000..f4c2769 Binary files /dev/null and b/loader/build/loadertest-c16.d71 differ diff --git a/loader/build/loadertest-c16.d81 b/loader/build/loadertest-c16.d81 new file mode 100644 index 0000000..f9bc49d Binary files /dev/null and b/loader/build/loadertest-c16.d81 differ diff --git a/loader/build/loadertest-c64.d64 b/loader/build/loadertest-c64.d64 new file mode 100644 index 0000000..a193a85 Binary files /dev/null and b/loader/build/loadertest-c64.d64 differ diff --git a/loader/build/loadertest-c64.d71 b/loader/build/loadertest-c64.d71 new file mode 100644 index 0000000..14c5d77 Binary files /dev/null and b/loader/build/loadertest-c64.d71 differ diff --git a/loader/build/loadertest-c64.d81 b/loader/build/loadertest-c64.d81 new file mode 100644 index 0000000..67a52be Binary files /dev/null and b/loader/build/loadertest-c64.d81 differ diff --git a/loader/build/minexample-c64.d64 b/loader/build/minexample-c64.d64 new file mode 100644 index 0000000..3c6004e Binary files /dev/null and b/loader/build/minexample-c64.d64 differ diff --git a/loader/build/transient-c64.prg b/loader/build/transient-c64.prg new file mode 100644 index 0000000..e69de29 diff --git a/loader/docs/Prerequisites.txt b/loader/docs/Prerequisites.txt new file mode 100755 index 0000000..a3e6cae --- /dev/null +++ b/loader/docs/Prerequisites.txt @@ -0,0 +1,27 @@ + +Path, loader only: +Name Source Remarks + +ca65 https://cc65.github.io set path to /bin +ld65 https://www.cc65.org/ set path to /bin +ar65 https://www.cc65.org/ set path to /bin + +make https://www.gnu.org/software/make/ +gcc https://gcc.gnu.org/ needed to build cc1541 and most of the compressors + +perl https://www.perl.org/ needed to generate a symbols file for the make prg target in loader/src and + to run the compressed file converter for Taboo Levelcrush +python https://www.python.org/ needed to run tinycrunch's tc_encode.py +rust https://www.rust-lang.org/ needed to build NuCrunch +zip needed to make a loader binary archive for the make prg target in loader/src + + +Path, samples folder: +Name Source Remarks + +lua https://www.lua.org/ needed to run the benchmarks + +c1541 https://vice-emu.sourceforge.net/ +vice https://vice-emu.sourceforge.net/ +plus4emu https://plus4emu.sourceforge.net/ needed if not using the USE_VICE=1 make run switch on Plus/4 and MAC OS X +Yape/YapeWin64 http://yape.homeserver.hu/ needed if not using the USE_VICE=1 make run switch on Plus/4 and Windows diff --git a/loader/docs/Protocol.txt b/loader/docs/Protocol.txt new file mode 100755 index 0000000..af139f3 --- /dev/null +++ b/loader/docs/Protocol.txt @@ -0,0 +1,77 @@ +Two bits + ATN loader protocol +------------------------------ + + <---------------------------- initiate loading ---------------------->| + |<--------- send name loop --------->| + | | + idle request | wait for | send filename chars to the drive | clear | busy + service | drive ready | | | + | | | | +Host DATA IN 0 _____ ... ____|¯¯¯¯|¯ ... ¯¯¯¯¯¯¯|¯¯|___|¯¯¯|___|¯¯¯|___|¯¯¯|___|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | __ __ __ __ __ __ __ __ | | | +Host CLK IN 0 _____ ... _________|_ ... ___|¯¯¯|...D0..D1..D2..D3..D4..D5..D6..D7...|......¯¯¯¯¯|¯¯¯¯|____|__ 0 + | | | | | +Host DATA OUT 1 ¯¯¯¯¯ ... ¯¯¯¯|____|_ ... _______|__|¯¯¯|___|¯¯¯|___|¯¯¯|___|¯¯¯|_____|___________|_________|__ 0 + | | | | | +Host CLK OUT 1 ¯¯¯¯¯ ... ¯¯¯¯|____|_ ... _______|...D0..D1..D2..D3..D4..D5..D6..D7...|......_____|_________|__ 0 + | | | | | +Host ATN OUT 1 ¯¯¯¯¯ ... ¯¯¯¯¯¯¯¯¯|¯ ... ¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | | | + | | | | | +Drive ATN IN 1 ¯¯¯¯¯ ... ¯¯¯¯¯¯¯¯¯|¯ ... ¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | | | +Drive ATNA OUT 1 ¯¯¯¯¯ ... ¯¯¯¯¯¯¯¯¯|¯ ... ¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | | | +Drive CLK OUT 1 ¯¯¯¯¯ ... ¯¯¯¯¯¯¯¯¯|¯ ... ¯¯¯|___|____________________________________|___________|____|¯¯¯¯|¯¯ 1 + | | | | | +Drive CLK IN 1 ¯¯¯¯¯ ... ¯¯¯¯¯¯¯¯¯|¯ ... ¯¯¯|___|...D0..D1..D2..D3..D4..D5..D6..D7...|......_____|____|¯¯¯¯|¯¯ 1 + | | | | | +Drive DATA OUT 0 _____ ... _________|_ ... _______|____________________________________|___________|_________|__ 0 + | | | | | +Drive DATA IN 1 ¯¯¯¯¯ ... ¯¯¯¯|____|_ ... _______|__|¯¯¯|___|¯¯¯|___|¯¯¯|___|¯¯¯|_____|___________|_________|__ 0 + | | | | | + host: DATA | drive: ack | host: clock out bits with CLK OUT | host: | drive: | + OUT 1 -> 0, | by CLK OUT | as data, DATA OUT as clock, | CLK | busy by | + CLK OUT | 1 -> 0 | filename is FILENAME_MAXLENGTH | OUT = 0 | CLK OUT | + 1 -> 0 | | chars or 0-terminated if fewer | | 0 -> 1 | + + + |<-------------------- fetch block or status (looped) -------------------->| + | |<-- get block loop -->| | + | | | | + busy | wait for | send block | get a byte from | drive | ack | acknowledge | idle + | the drive's | start signal | the drive | busy | block | file transfer | + | response | to the drive | | | | | + | | | | | | | +Host DATA IN 1 ¯¯¯¯¯¯|¯ ... ¯¯¯|_____|_______________|..D1...D3...D5...D7...|......¯¯¯¯¯|¯¯¯|___|_______________|__ 0 + | | | | | | | +Host CLK IN 0 ______|_ ... _____|¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|..D0...D2...D4...D6...|......_____|_______|_______________|__ 0 + | | | | | | | +Host DATA OUT 0 ______|_ ... _________|_______________|______________________|___________|_______|_______|¯¯¯¯¯¯¯|¯¯ 1 + | | | | | | | +Host CLK OUT 0 ______|_ ... _________|_______________|______________________|___________|_______|_______|¯¯¯¯¯¯¯|¯¯ 1 + | | | | | | | +Host ATN OUT 1 ¯¯¯¯¯¯|¯ ... ¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|_______|_____|¯¯¯¯|____|¯¯¯¯|_|___________|___|¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | | | | | + | | | | | | | +Drive ATN IN 1 ¯¯¯¯¯¯|¯ ... ¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|_______|_____|¯¯¯¯|____|¯¯¯¯|_|___________|___|¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | | | | | +Drive ATNA OUT 1 ¯¯¯¯¯¯|¯ ... ¯¯¯|_XXXX|XXXXXXXXXXXXXXX|¯|____|¯¯¯¯|____|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | __ __ __ __ | | | | +Drive CLK OUT 1 ¯¯¯¯¯¯|¯ ... ¯¯¯¯¯|___|_______________|..D0...D2...D4...D6...|......¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | __ __ __ __ | | | | +Drive CLK IN 1 ¯¯¯¯¯¯|¯ ... ¯¯¯¯¯|___|_______________|..D0...D2...D4...D6...|......¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯ 1 + | | | __ __ __ __ | | | | +Drive DATA OUT 0 ______|_ ... ___|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|..D1...D3...D5...D7...|......_____|_______|_______________|__ 0 + | | | __ __ __ __ | | | | +Drive DATA IN 0 ______|_ ... ___|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|..D1...D3...D5...D7...|......_____|_______|_______|¯¯¯¯¯¯¯|¯¯ 1 + | | | | | | | + | drive: signal | host: ATN | host: clock in | drive: | host: | host: ack by | + | ready by DATA | OUT 1 -> 0 | bit pairs with | DATA | ATN | DATA OUT | + | OUT 0 -> 1, | | ATN OUT as clock, | OUT = 0, | OUT | and CLK OUT | + | CLK OUT 1->0, | | last bit pair | CLK | 0->1 | 0 -> 1: enter | + | then, if host | | acknowledged with | OUT = 1 | | idle mode = | + | DATA IN is | | ATN OUT 1 -> 0 | | | host ATN, CLK | + | set to 1, the | | | | | and DATA OUT | + | drive is not | | | | | = 1, drive | + | present | | | | | ATNA OUT = 1 | diff --git a/loader/include/config.inc b/loader/include/config.inc new file mode 100755 index 0000000..3c4d610 --- /dev/null +++ b/loader/include/config.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::ZX0; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 1 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays (take care for update race conditions). + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this option reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/include/loader.inc b/loader/include/loader.inc new file mode 100755 index 0000000..f38ebb8 --- /dev/null +++ b/loader/include/loader.inc @@ -0,0 +1,707 @@ + +.ifndef _LOADER_INC_ +_LOADER_INC_ = 1 + +DISABLE_WATCHDOG = 0 ; disable the drive-side watchdog - the watchdog will reset the drive safely when the host resets at any point, + ; however, the drive hardware (1541 and 1571) only allows for a maximum time-out period of 65536 cycles: this means + ; that letting the loader starve for a few video frames (about 1.5 frames with a 1571 running at 2 MHz) will reset + ; the drive, which can be prevented using this option. + +; C-128 only: for burst transfers, use the CLK line for the incoming data-sent signal rather than the +; SERIAL_IRQ flag in CIA1_ICR ($dc0d) in order to avoid interference by CIA1 interrupt handlers, +; note that this slows down transfer speed +ASYNCHRONOUS_BURST_HANDSHAKE = 0 + +; set to 0 if loading via KERNAL fallback is problematic with non-standard KERNALs, VICE virtual drives, IEEE-488 interfaces or devices like IDE64 +KERNAL_FALLBACK_SEI_WORKAROUNDS = 1 +KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS = 0; this does not work with VICE virtual device traps + +CONFIG_INTERNAL = ((KERNAL_FALLBACK_SEI_WORKAROUNDS = 0) << 3) | (KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS << 2) | (ASYNCHRONOUS_BURST_HANDSHAKE << 1) | DISABLE_WATCHDOG + +.pushseg + +.segment "CODE"; import symbols as absolute by default + +.scope diskio + +.scope platform + COMMODORE_64 = 64 + COMMODORE_128 = 128 + COMMODORE_16 = 16 +.endscope; platform + +.scope drivetype + DRIVES_1541 = $00 + DRIVES_157X = $10 + DRIVES_1581_CMD = $20 + DRIVES_MASK = $f0 + + DRIVE_1541 = $01 + DRIVE_1541_C = $02 + DRIVE_1541_II = $03 + DRIVE_1541U = $04 + DRIVE_1570 = $15 + DRIVE_1571 = $16 + DRIVE_1571CR = $17 + DRIVE_1581 = $28 + DRIVE_CMD_FD_2000 = $29 + DRIVE_CMD_FD_4000 = $2a + DRIVE_CMD_HD = $2b + + DRIVE_GENERIC_SERIAL = $fc ; -4 + DRIVE_GENERIC_BURST = $fd ; -3 + DRIVE_GENERIC_PARALLEL = $fe ; -2 + DEVICE_NONE = $ff ; -1 +.endscope; drivetype + +.scope status + OK = 0 + + FILE_TOO_LARGE = $f7 ; -9 + FILE_ON_DISK_TOO_SMALL = $f8 ; -8 + FILE_ON_DISK_TOO_LARGE = $f9 ; -7 + WRITE_PROTECT_ON = $fa ; -6 + + DEVICE_INCOMPATIBLE = $fb ; -5, cannot upload loader drive code to active device, if LOAD_VIA_KERNAL_FALLBACK != 0, do not regard this as an error + TOO_MANY_DEVICES = $fc ; -4, a device on the bus other than the active device is not compatible, if LOAD_VIA_KERNAL_FALLBACK != 0, do not regard this as an error + GENERIC_KERNAL_ERROR = $fd ; -3, an error occured during installation or while loading without installed drive code via KERNAL fallback, check the x register for further information + DEVICE_NOT_PRESENT = $fe ; -2 + FILE_NOT_FOUND = $ff ; -1 +.endscope; status + +.endscope; diskio + +.enum DECOMPRESSORS + + BITNAX = 1 ; Doynax and Bitbreaker's Bitnax + BYTEBOOZER2 = 2 ; HCL/Booze Design's ByteBoozer2 + DOYNAX_LZ = 3 ; Doynax LZ, aka Doynamite + EXOMIZER = 4 ; Magnus Lind's Exomizer + LEVELCRUSH = 5 ; Taboo Levelcrush + LZSA2 = 6 ; Emmanuel Marty's LZSA2 with modifications by Bitbreaker + NUCRUNCH = 7 ; ChristopherJam aka Shrydar's NuCrunch + PUCRUNCH = 8 ; Pasi Ojala's Pucrunch + SUBSIZER = 9 ; tlr's Subsizer + TINYCRUNCH = 10 ; ChristopherJam aka Shrydar's tinycrunch + TSCRUNCH = 11 ; Antonio Savona's TSCrunch + ZX0 = 12 ; Einar Saukas' ZX0/Salvador with Dali modifications by Bitbreaker + + NONE = 0 + +.endenum + + +.ifdef EXTCONFIGPATH + .include "loaderconfig.inc" +.else + .include "config.inc" +.endif; !EXTCONFIGPATH + +.if .defined(FORCE_ASYNCHRONOUS_BURST_HANDSHAKE) +USE_ASYNCHRONOUS_BURST_HANDSHAKE = 1 +.else +USE_ASYNCHRONOUS_BURST_HANDSHAKE = ASYNCHRONOUS_BURST_HANDSHAKE +.endif + + +.ifndef __NO_LOADER_SYMBOLS_IMPORT +.ifndef install + +.if PLATFORM <> diskio::platform::COMMODORE_16 + ; Set the VIC bank + ; in: a - VIC bank (0..3) + ; out: undefined + .macro SET_VIC_BANK bank + lda #bank & 3 + sta CIA2_PRA + .endmacro + + ; allow for arbitrary $DD00 writes ($00-$FF) when the loader is idle, + ; good for raster routines with LDA #value:STA $D018:STA $DD00, e.g. + + ; call after load + .macro ENTER_BUS_LOCK + ; nothing to do + .endmacro + + ; call before load + .macro LEAVE_BUS_LOCK + ; when the loader is idle, the user is + ; allowed to write anything to CIA2_DDRA ($DD00) - + ; set it to a known and valid state here. + ;php; without these, there's a race condition and thus a small + ;sei; chance for video glitches, but with these, there's a + INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + ;plp; chance for actual crashes with stable raster routines + .endmacro + + .if ALLOW_2_MHZ_ON_C128 | (PLATFORM = diskio::platform::COMMODORE_128) + ; For 2 MHz in interrupt handlers during loading, + ; to be run at the start of every IRQ handler + .macro LOADER_IRQ_HANDLER_PROLOGUE + ; buffer clock setting and continue at 2 MHz + lda VIC2_C128_CLOCK + pha + lda #C128_TWO_MHZ + sta VIC2_C128_CLOCK + .endmacro + + ; For 2 MHz in interrupt handlers during loading, + ; to be run at the end of every IRQ handler + .macro LOADER_IRQ_HANDLER_EPILOGUE + pla + sta VIC2_C128_CLOCK + .endmacro + .endif +.else + ; To be run at the start of every IRQ handler + .macro LOADER_IRQ_HANDLER_PROLOGUE + ; buffer force single clock flag + ; and allow double clock + lda TED_CHARGEN_ADDR + pha + and #255 - FORCE_SINGLE_CLOCK + sta TED_CHARGEN_ADDR + .endmacro + + ; To be run at the end of every IRQ handler + .macro LOADER_IRQ_HANDLER_EPILOGUE + ; restore force single clock flag + .local fast + + pla + lsr + lsr + lda TED_CHARGEN_ADDR + and #255 - FORCE_SINGLE_CLOCK + bcc fast + ora #FORCE_SINGLE_CLOCK +fast: sta TED_CHARGEN_ADDR + .endmacro +.endif + + +; Install the loader +; note: KERNAL ROM must be enabled, and the I flag will be cleared (CLI) +; in: nothing +; out: c - set on error +; a - status +; x - drive type (one of diskio::drivetypes) +; y - if status is diskio::status::OK, zeropage address of version string address +.import install +.macro LOADER_INSTALL + jsr install +.endmacro + +.if LOAD_RAW_API +; Load a file without decompression +; in: x/y - x: lo, y: hi to 0-terminated filename string, +; zero-length file name will load next file +; c - if LOAD_TO_API != 0, c = 0: load to address as stored in the file +; c = 1: load to caller-specified address (loadaddrlo/hi) +; out: c - set on error +; a - status +.import loadraw +.macro LOADRAW name + .if LOAD_TO_API + clc + .endif + ldx #<(name) + ldy #>(name) + jsr loadraw +.endmacro + +.macro LOADNEXTRAW + lda #0 + LOADRAW * - 1 +.endmacro + +.if LOAD_TO_API + .macro LOADRAW_LOADTO name, dest + sec + lda #<(dest) + sta loadaddrlo + lda #>(dest) + sta loadaddrhi + ldx #<(name) + ldy #>(name) + jsr loadraw + .endmacro + + .macro LOADNEXTRAW_LOADTO dest + lda #0 + LOADRAW_LOADTO * - 1, dest + .endmacro +.endif; LOAD_TO_API +.endif; LOAD_RAW_API + +.if LOAD_COMPD_API +; Load a compressed file +; in: x/y - x: lo, y: hi to 0-terminated filename string, +; zero-length file name will load next file +; c - if DECOMPLOAD_TO_API != 0, c = 0: load to address as stored in the file +; c = 1: load with caller-specified address offset (loadaddroffslo/hi)* +; out: c - set on error +; a - status +; x/y - if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH, x: lo, y: hi of the loaded file's execution address + +; * this is an offset rather than an absolute destination, as the original destination address required for calculating this offset is +; stored in the file, but to retrieve it, the first file block would have to be loaded first to the original load address or to an extra buffer + +.import loadcompd +.import loadcompdopen +.import loadcompdexecute +.macro LOADCOMPD name + ldx #<(name) + ldy #>(name) + jsr loadcompd +.endmacro + +.macro LOADNEXTCOMPD + lda #0 + LOADCOMPD * - 1 +.endmacro + +.if LOAD_TO_API + .macro LOADCOMPD_RELTO name, load_address_offset + sec + lda #<(load_address_offset) + sta loadaddroffslo + lda #>(load_address_offset) + sta loadaddroffshi + ldx #<(name) + ldy #>(name) + jsr loadcompd + .endmacro + + .macro LOADNEXTCOMPD_RELTO dest_lo, dest_hi + lda #0 + LOADCOMPD_RELTO #<(* - 1), #>(* - 1), dest_lo, dest_hi + .endmacro +.endif; LOAD_TO_API +.endif; LOAD_COMPD_API + +.if FILE_EXISTS_API +; Check if file exists +; in: x/y - x: lo, y: hi to 0-terminated filename string +; out: c - set on file not found or error +; a - status +.import fileexists +.macro FILEEXISTS name + ldx #<(name) + ldy #>(name) + jsr fileexists +.endmacro +.endif; FILE_EXISTS_API + +.if MEM_DECOMP_API +; Decompress a compressed file from memory +; in: x/y - x: lo, y: hi of compressed file in memory +; c - if MEMDECOMP_TO_API != 0, c = 0: decompress to address as stored in the file +; c = 1: decompress to caller-specified address (loadaddrlo/hi) +; out: x/y - if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH, x: lo, y: hi of the file's execution address +.import memdecomp +.macro MEMDECOMP source_lo, source_hi + .if MEM_DECOMP_TO_API + clc + .endif + ldx source_lo + ldy source_hi + jsr memdecomp +.endmacro + +.if MEM_DECOMP_TO_API + .macro MEMDECOMP_TO source_lo, source_hi, dest + sec + lda #<(dest) + sta decdestlo + lda #>(dest) + sta decdesthi + ldx source_lo + ldy source_hi + jsr memdecomp + .endmacro +.endif; MEM_DECOMP_TO_API +.endif; MEM_DECOMP_API + +.endif; !install +.endif; !__NO_LOADER_SYMBOLS_IMPORT + +.struct drivecode + entry .word; drive + to .word; drive + length .word; bytes + from .word; host +.endstruct + +.struct swapparams + buffer .word; $0800 bytes for swapped loader drive code + + drivecode41 .tag drivecode + .if ONLY_1541_AND_COMPATIBLE = 0 + drivecode71 .tag drivecode + drivecode81 .tag drivecode + .endif; ONLY_1541_AND_COMPATIBLE = 0 +.endstruct + +.ifndef __NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT +.ifndef swapdrvcod +.import swapdrvcod +.import restoreldr +.endif; !swapdrvcod + +; Execute custom code in the drive, buffer loader code on host side before +; in: x/y - x: lo, y: hi of swapparams structure +; out: c - set on error +; a - status +.macro SWAP_DRIVECODE params + ldx #<(params) + ldy #>(params) + jsr swapdrvcod +.endmacro + +; Restore drive-side loader code +; in: nothing +; out: undefined +.macro RESTORE_LOADER + jsr restoreldr +.endmacro +.endif; !__NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT + +.struct saveparams + filename .word; existing file to overwrite, pointer to 0-terminated filename string + from .word; pointer to file data + length .word; length of file data in bytes + loadaddress .word; usually same as from + buffer .word; pointer to $0800 bytes for swapped loader drive code +.endstruct + +.ifndef __NO_SAVE_SYMBOLS_IMPORT +.ifndef save + +.import save +; Save a file by overwriting an existing file of the same block-size +; in: x/y - x: lo, y: hi of saveparams structure +; out: c - set on error +; a - status +.macro SAVE_OVERWRITE params + ldx #<(params) + ldy #>(params) + jsr save +.endmacro + +.endif; !save +.endif; !__NO_SAVE_SYMBOLS_IMPORT + +.ifndef __NO_LOADER_SYMBOLS_IMPORT + +.if UNINSTALL_API +; Uninstall the loader +; in: nothing +; out: undefined +.import uninstall +.macro LOADER_UNINSTALL + jsr uninstall +.endmacro +.endif; UNINSTALL_API + +.if CLOSE_FILE_API +; Close an open file +; in: nothing +; out: undefined +.import closefile +.endif; CLOSE_FILE_API + + +; linker-generated loader segments symbols + +.import __DISKIO_ZP_START__ +.import __DISKIO_ZP_END__ +.import __DISKIO_ZP_LOAD__ +.import __DISKIO_ZP_RUN__ +.import __DISKIO_ZP_SIZE__ + +.import __DISKIO_INSTALL_START__ +.import __DISKIO_INSTALL_END__ +.import __DISKIO_INSTALL_LOAD__ +.import __DISKIO_INSTALL_RUN__ +.import __DISKIO_INSTALL_SIZE__ + +.import __DISKIO_START__ +.import __DISKIO_END__ +.import __DISKIO_LOAD__ +.import __DISKIO_RUN__ +.import __DISKIO_SIZE__ + +.endif; !__NO_LOADER_SYMBOLS_IMPORT + + +.ifndef __NOIMPORTVARS +.ifndef loadaddrlo + .importzp loadaddrlo + .importzp loadaddrhi + + .importzp loadaddroffslo + .importzp loadaddroffshi + + .importzp decdestlo + .importzp decdesthi + + .importzp endaddrlo + .importzp endaddrhi + + .importzp bytesloadedlo + .importzp bytesloadedhi +.endif; !loadaddrlo +.endif; !__NOIMPORTVARS + +.if PLATFORM <> diskio::platform::COMMODORE_16 + .macro INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + ; this is executed during install and LEAVE_BUS_LOCK, + ; hence it accesses the port register rather than the data direction register + lda #3 + and CIA2_PRA + sta CIA2_PRA + .endmacro +.endif + +; custom drive code API + +.ifndef ID041 + +.importzp ID041 +.importzp CURRTRACK41 +.importzp NUMFILES41 +.importzp SECTORLINKTABLE41 + +.import topofstack41 +.import idleloop41 +.import getbytecmp41 +.import BLOCKBUFFER41 + +.importzp V1B41 +.importzp V2B41 +.importzp LEDSTATE41 +.importzp FILESECTOR41 +.importzp FILENAME41 +.importzp LINKTRACK41 +.importzp LINKSECTOR41 +.importzp REQUESTEDSECTOR41 +.import trkseek41 +.import initlink41 +.import sertoraw41 +.import getbytewdog41 +.import getbyte41 +.import getbyterts41 +.import setbv2b41 +.import getblock41 +.import idxloop41 +.import wdogentr41 +.import findfile41 +.import loadfile41 + +.endif + +; Return-to-loader macros +; upon execution, i-flag must be set, ATN must be asserted, DATA and CLK must be cleared + +.macro RETURNTOLOADER41 hook + .scope + .assert * >= $0100, error, "Return to 1541 loader code too low in memory" + + lda #$1a; ATNA_OUT | CLK_OUT | DATA_OUT + sta $1800; VIA1_PRB + + lda $1800; VIA1_PRB + and #$60; DEVICE_NUMBER + ora #$94; ATN_IN | ATNA_OUT | CLK_IN + sta compare + 1 + ldx #0 + + lda #$10; ATNA_OUT + sta $1800; VIA1_PRB + + ldy $1800; VIA1_PRB +getbyte: lda #$80 +getbit: cpy $1800; VIA1_PRB + beq getbit + ldy $1800; VIA1_PRB +compare: cpy #0 + ror + bcc getbit + sta $00,x + inx + bne getbyte + + hook + + jmp $00da + + .assert * <= $0800, error, "Return to 1541 loader code too high in memory" + .endscope +.endmacro + +.ifndef NUMFILES71 + +.importzp CURRTRACK71 +.importzp LEDSTATE71 +.importzp CLEARSECTORLINKTABLE71 +.importzp FILENAME71 +.importzp LINKTRACK71 +.importzp LINKSECTOR71 +.importzp SECTORLINKTABLE71 +.importzp DIRSECTORS71 +.importzp CUSTOMZPBUFFER71 +.importzp CUSTOMUPLOADSIZE71 +.importzp getbytrt71 +.importzp CUSTOMPARAM71 + +.importzp bsetv2b71 +.importzp trkseek71 +.import initlink71 +.import onemhz71 +.import topofstack71 +.import getblock71 +.import idleloop71 +.import findfile71 +.import loadfile71 +.import idxloop71 +.import BLOCKBUFFER71 + +.endif + +.macro RETURNTOLOADER71 hook + .scope + .assert * >= $0100, error, "Return to 1571 loader code too low in memory" + + ldx #0 + + lda #$10; ATNA_OUT + sta $1800 + + ldy $1800; VIA1_PRB +getbyte: lda #$80 +getbit: cpy $1800; VIA1_PRB + beq getbit + ldy $1800; VIA1_PRB + cpy #$94; ATN_IN | ATNA_OUT | CLK_IN + ror + bcc getbit + sta $00,x + inx + bne getbyte + + hook + + jmp $0037 + + .assert * <= $0800, error, "Return to 1571 loader code too high in memory" + .endscope +.endmacro + +.ifndef dcodinit81 + +.importzp CUSTOMPARAM81 + +.import dcodinit81 +.import DRVCODEND81 +.import BLOCKBUFFER81 +.import CUSTOMRECEIVE81 +.import filename81 +.import swapzp81 +.import getblock81 +.import initwdog81 +.import enablwdg81 +.import initcntr81 +.import bsyledon81 +.import findfile81 +.import loadfile81 +.import DIRTRACKS81 + +.endif + +.macro RETURNTOLOADER81 hook + .scope + .assert * >= DRVCODEND81, error, "Return to 1581 loader code too low in memory" + + ldy #0 + sty $4001; CIA_PRB +getbyte: lda #$80 +getbit: pha + lda $4001; CIA_PRB +waitbit: cmp $4001; CIA_PRB + beq waitbit + lda $4001; CIA_PRB + and #$04; CLK_IN + cmp #$04; CLK_IN + pla + ror + bcc getbit +restore: sta $0300 & $ff00,y + iny + bne :+ + inc restore + 2 +: cpy #DRVCODEND81 + bne getbyte + + hook + +waitrun: lda $4001; CIA_PRB + lsr + bcc waitrun + jmp dcodinit81 + + .endscope +.endmacro + +.popseg + +.if (ALLOW_2_MHZ_ON_C128 & (PLATFORM = diskio::platform::COMMODORE_64)) | (PLATFORM = diskio::platform::COMMODORE_128) + USE_2_MHZ = 1 +.else + USE_2_MHZ = 0 +.endif + +.if LOAD_VIA_KERNAL_FALLBACK + .if DIRTRACK <> 18 + .error "***** Option LOAD_VIA_KERNAL_FALLBACK requires DIRTRACK to be 18 *****" + .endif + .if DIRTRACK81 <> 40 + .error "***** Option LOAD_VIA_KERNAL_FALLBACK requires DIRTRACK81 to be 40 *****" + .endif + + .if (PLATFORM = diskio::platform::COMMODORE_128) & ASYNCHRONOUS_BURST_HANDSHAKE + .error "***** Option LOAD_VIA_KERNAL_FALLBACK requires ASYNCHRONOUS_BURST_HANDSHAKE = 0 *****" + .endif + + .macro CHECK_LOADER_ZP_ADDRESSES + .assert (loader_zp_first > STATUS) || (loader_zp_last < STATUS), error, "Loader zeropage variables cross KERNAL STATUS register at $90" + .assert (loader_zp_first > C3PO) || (loader_zp_last < C3PO), error, "Loader zeropage variables cross KERNAL serial buffer output char buffered flag C3PO at $94" + .assert (loader_zp_first > BSOUR) || (loader_zp_last < BSOUR), error, "Loader zeropage variables cross KERNAL serial buffer byte BSOUR at $95" + + .if PLATFORM = diskio::platform::COMMODORE_16 + .assert (loader_zp_first > LDTND) || (loader_zp_last < LDTND), error, "Loader zeropage variables cross KERNAL logical file index/open files count at $97" + .assert (loader_zp_first > DFLTN) || (loader_zp_last < DFLTN), error, "Loader zeropage variables cross KERNAL input device variable DFLTN at $98" + .assert (loader_zp_first > DFLTO) || (loader_zp_last < DFLTO), error, "Loader zeropage variables cross KERNAL output device variable DFLTO at $99" + .assert (loader_zp_first > R2D2) || (loader_zp_last < R2D2), error, "Loader zeropage variables cross KERNAL serial bus EOI flag R2D2 at $a6" + .assert (loader_zp_first > BSOUR1) || (loader_zp_last < BSOUR1), error, "Loader zeropage variables cross KERNAL serial bus shift counter BSOUR1 at $a8" + .assert (loader_zp_first > COUNT) || (loader_zp_last < COUNT), error, "Loader zeropage variables cross KERNAL serial bus counter COUNT at $aa" + .assert (loader_zp_first > USE4DY) || (loader_zp_last < USE4DY), error, "Loader zeropage variables cross KERNAL parallel drive state register USE4DY at $f9" + .else; PLATFORM <> diskio::platform::COMMODORE_16 + .assert (loader_zp_first > LDTND) || (loader_zp_last < LDTND), error, "Loader zeropage variables cross KERNAL logical file index/open files count at $98" + .assert (loader_zp_first > DFLTN) || (loader_zp_last < DFLTN), error, "Loader zeropage variables cross KERNAL input device variable DFLTN at $99" + .assert (loader_zp_first > DFLTO) || (loader_zp_last < DFLTO), error, "Loader zeropage variables cross KERNAL output device variable DFLTO at $9a" + .assert (loader_zp_first > R2D2) || (loader_zp_last < R2D2), error, "Loader zeropage variables cross KERNAL serial bus EOI flag R2D2 at $a3" + .assert (loader_zp_first > BSOUR1) || (loader_zp_last < BSOUR1), error, "Loader zeropage variables cross KERNAL serial bus shift counter BSOUR1 at $a4" + .assert (loader_zp_first > COUNT) || (loader_zp_last < COUNT), error, "Loader zeropage variables cross KERNAL serial bus counter COUNT at $a5" + .endif; PLATFORM <> diskio::platform::COMMODORE_16 + .endmacro +.else + .macro CHECK_LOADER_ZP_ADDRESSES + .endmacro +.endif + +.endif; _LOADER_INC_ diff --git a/loader/samples/benchmark/Makefile b/loader/samples/benchmark/Makefile new file mode 100644 index 0000000..a55fa51 --- /dev/null +++ b/loader/samples/benchmark/Makefile @@ -0,0 +1,319 @@ + +NAME = benchmark +BENCH ?= BITFIRE +LOADERCPU ?= 100 +ALIGNED ?= 0 +_PLATFORM_ = c64 + +export LOG + +BUILDDIR = ../../build +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(LOADERCPU)-$(_PLATFORM_).d64 + +default: $(DISKIMAGE) + +run_bitfire: + make run BENCH=BITFIRE + +run_spindle_code: + make run BENCH=SPINDLE_CODE + +run_spindle_graphics: + make run BENCH=SPINDLE_GRAPHICS + +perfbench_bitfire: + make perfbench BENCH=BITFIRE + +perfbench_spindle_code: + make perfbench BENCH=SPINDLE_CODE + +perfbench_spindle_graphics: + make perfbench BENCH=SPINDLE_GRAPHICS + +perfbench: LOG=1 +perfbench: + rm -f benchmark.log + make clean + make run LOADERCPU=100 + make run LOADERCPU=90 + make run LOADERCPU=80 + make run LOADERCPU=70 + make run LOADERCPU=60 + make run LOADERCPU=50 + make run LOADERCPU=40 + make run LOADERCPU=30 + make run LOADERCPU=20 + make run LOADERCPU=10 + $(MAKE_TEX) < benchmark.log + +perftest: LOG=1 +perftest: + rm -f benchmark.log + make clean + make run LOADERCPU=100 + $(MAKE_TEX) < benchmark.log + +ifeq "$(BENCH)" "BITFIRE" +MONCOMMANDS := benchmark-bitfire.mon +MAKE_TEX := ./make-tex-graph-bitfire.lua +else +MONCOMMANDS := benchmark-spindle.mon +ifeq "$(BENCH)" "SPINDLE_CODE" +MAKE_TEX := ./make-tex-graph-spindle-code.lua +else +MAKE_TEX := ./make-tex-graph-spindle-graphics.lua +endif +endif + +ifeq "$(LOG)" "1" +VICE = x64sc +saveres -warp -minimized -autostartprgmode 1 +cart -debugcart -monlogname benchmark.log -moncommands $(MONCOMMANDS) +else +VICE = x64sc +endif +EMU = $(VICE) -drive8type 1541 -drive9type 0 -8 +EMU71 = $(VICE) -drive8type 1571 -drive9type 0 -8 +EMU81 = $(VICE) -drive8type 1581 -drive9type 0 -8 + +AS = cl65 +AS_FLAGS = -Wa -I../../build -u __EXEHDR__ + +CC1541 = ../../tools/cc1541/cc1541 -v +#CC1541 = ../../tools/cc1541/repo4/cc1541/cc1541 -v +CC1541_SOURCE = ../../tools/cc1541 + +B2 = ../../tools/b2/b2.exe +B2_SOURCE = ../../tools/b2 + +BITNAX = ../../tools/bitnax-07a8c67/lz + +DOYNAX_LZ = ../../tools/doynamite1.1/lz + +EXO = ../../tools/exomizer-3.1/src/exomizer +EXO_SOURCE = ../../tools/exomizer-3.1/src + +LC = ../../tools/wcrush/wcrush/wcrush +LCSPEED = 6 +LC_SOURCE = ../../tools/wcrush + +LZSA2 = ../../tools/lzsa/lzsa +LZSA2_SOURCE = ../../tools/lzsa + +NC = ../../tools/nucrunch-1.0.1/target/release/nucrunch +NC_SOURCE = ../../tools/nucrunch-1.0.1 + +PU = ../../tools/pucrunch/pucrunch +PU_SOURCE = ../../tools/pucrunch + +SUBSIZER = ../../tools/subsizer-0.7pre1/subsizer +SUBSIZER_SRC = ../../tools/subsizer-0.7pre1/src + +TC = python ../../tools/tinycrunch_v1.2/tc_encode.py + +TS_ENCODER = ../../tools/tscrunch/tscrunch.py +TS = python3 $(TS_ENCODER) + +ZX0 = ../../tools/dali/dali +ZX0_SRC = ../../tools/dali + +CONV = ../../tools/compressedfileconverter.pl + +RM = rm -f +CP = cp +MV = mv + +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/loader-$(_PLATFORM_).prg + +RESOURCESDIR = ../resources +TEST = $(RESOURCESDIR)/test.prg + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-$(LOADERCPU)-uncompressed-$(_PLATFORM_).prg +DISKIMAGE81 = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d81 + +EXTENSION_NONE := prg +EXTENSION_BITNAX := bnx +EXTENSION_BYTEBOOZER2 := b2 +EXTENSION_DOYNAX_LZ := dnx +EXTENSION_EXOMIZER := exo +EXTENSION_LEVELCRUSH := lc +EXTENSION_LZSA2 := lzsa2 +EXTENSION_NUCRUNCH := nc +EXTENSION_PUCRUNCH := pu +EXTENSION_SUBSIZER := ssz +EXTENSION_TINYCRUNCH := tc +EXTENSION_TSCRUNCH := ts +EXTENSION_ZX0 := zx0 + +COMPRESSOR := $(shell grep -m 1 'DECOMPRESSORS::' $(LOADERCFG) | sed 's/.*DECOMPRESSORS::\([^; ]*\).*/\1/') +COMPEXT := $(EXTENSION_$(COMPRESSOR)) + +BENCH_IDX_BITFIRE = 0 +FILENAMES_BITFIRE = test a b c d e f g h i j k l m n o p q r +MINTRACKS_BITFIRE = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +INTERLEAVES_BITFIRE = 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 +#INTERLEAVES_BITFIRE = -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 +#INTERLEAVES_BITFIRE = -4 -4 -4 -4 -4 -4 -4 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 + +BENCH_IDX_SPINDLE_CODE = 1 +FILENAMES_SPINDLE_CODE = test x0 x1 x2 x3 x4 x5 x6 x7 +MINTRACKS_SPINDLE_CODE = 1 1 1 1 1 1 1 1 1 +INTERLEAVES_SPINDLE_CODE = 4 4 4 4 4 4 4 4 4 + +BENCH_IDX_SPINDLE_GRAPHICS = 2 +FILENAMES_SPINDLE_GRAPHICS = test y0 y1 y2 y3 y4 y5 y6 y7 +MINTRACKS_SPINDLE_GRAPHICS = 1 1 1 1 1 1 1 1 1 +INTERLEAVES_SPINDLE_GRAPHICS = 4 4 4 4 4 4 4 4 4 + +FILES = $(patsubst %, $(INTERMDIR)/%.$(COMPEXT), $(FILENAMES_$(BENCH))) +IMAGEFILES__ = $(foreach filename, $(FILENAMES_$(BENCH)),$(patsubst %,^-f^"%",$(filename).$(COMPEXT))$(patsubst %,^-w^$(INTERMDIR)/%.$(COMPEXT),$(filename))) +IMAGEFILES_ = $(subst "test.$(COMPEXT)","test", $(IMAGEFILES__)) +IMAGEFILES = $(subst ^, ,$(join $(addprefix -r^,$(MINTRACKS_$(BENCH))),$(join $(addprefix ^-S^,$(INTERLEAVES_$(BENCH))),$(patsubst %,^%,$(IMAGEFILES_))))) +IMAGEFILES81 = $(subst ^, ,$(join $(addprefix -r^,$(MINTRACKS_$(BENCH))),$(patsubst %,^%,$(IMAGEFILES_)))) + +ifeq "$(COMPRESSOR)" "NONE" +$(DISKIMAGE): $(ASSEMBLE) $(FILES) $(CC1541) + $(RM) $@ + $(CC1541) -n "normal is boring" -i plush -4 \ + $(IMAGEFILES) \ + -f test-loopfile -l test \ + -f test-loopfile2 -l test \ + $@ +else +$(DISKIMAGE): $(ASSEMBLE) $(FILES) $(CC1541) + $(RM) $@ + $(CC1541) -n "normal is boring" -i plush \ + $(IMAGEFILES) \ + -f test-loopfile -l test \ + -S 4 -r 29 -e -f $(NAME) -w $< \ + -g $(basename $@).g64 \ + $@ +endif +# -w $(RESOURCESDIR)/room_0031.prg \ +# -w $(RESOURCESDIR)/0031.zx0.prg \ +# -w $(RESOURCESDIR)/zb \ + +$(DISKIMAGE81): $(ASSEMBLE) $(FILES) $(CC1541) + $(RM) $@ + $(CC1541) -n "normal is boring" -i plush \ + -r 39 -f $(NAME) -w $< \ + $(IMAGEFILES81) \ + $@ + +$(ASSEMBLE): $(SOURCE) $(LOADER) + $(AS) $(AS_FLAGS) -C c64-asm.cfg -Wa -DPLATFORM=64 -Wa -DBENCHMARK=$(BENCH_IDX_$(BENCH)) -Wa -DLOADERCPU=$(LOADERCPU) -o $@ $< + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) PLATFORM=$(_PLATFORM_) PROJECT=$(NAME) INSTALL=0f00 RESIDENT=0a00 ZP=02 prg + +run: $(DISKIMAGE) +ifeq "$(ALIGNED)" "0" + $(EMU) $(realpath $^) $(ASSEMBLE) +else + $(EMU) $(basename $(realpath $^)).g64 $(ASSEMBLE) +endif + +run71 run70: $(DISKIMAGE) + $(EMU71) $(realpath $^) $(ASSEMBLE) + +run81: $(DISKIMAGE81) + $(EMU81) $(realpath $^) $(ASSEMBLE) + +clean: + -$(RM) *.o $(ASSEMBLE) $(DISKIMAGE) $(BUILDDIR)/loadersymbols-c64.prg $(BUILDDIR)/install-c64.prg $(BUILDDIR)/loader-c64.prg \ + $(INTERMDIR)/test.prg \ + $(INTERMDIR)/*.b2 $(INTERMDIR)/*.bnx $(INTERMDIR)/*.dnx $(INTERMDIR)/*.exo $(INTERMDIR)/*.lc $(INTERMDIR)/*.lzsa2 $(INTERMDIR)/*.nc $(INTERMDIR)/*.pu $(INTERMDIR)/*.ssz $(INTERMDIR)/*.tc $(INTERMDIR)/*.ts $(INTERMDIR)/*.zx0 + + +$(CC1541): $(CC1541_SOURCE)/cc1541.c + $(MAKE) -C $(CC1541_SOURCE) cc1541 + + +$(INTERMDIR)/%.prg: $(RESOURCESDIR)/% + $(CP) $^ $@ + +EXTENSIONS := prg bnx b2 dnx exo lc lzsa2 nc pu ssz tc ts zx0 + +define COPY +$(INTERMDIR)/x%.$(1): $(INTERMDIR)/x.$(1) + $(CP) $$^ $$@ + +$(INTERMDIR)/y%.$(1): $(INTERMDIR)/y.$(1) + $(CP) $$^ $$@ +endef + +$(foreach extension,$(EXTENSIONS),$(eval $(call COPY,$(extension)))) + +$(INTERMDIR)/%.b2: $(INTERMDIR)/%.prg $(B2) + $(B2) $< + $(MV) $<.b2 $@ + +$(INTERMDIR)/%.bnx: $(INTERMDIR)/%.prg $(BITNAX) + $(BITNAX) --bitfire -o $@ $< + +$(INTERMDIR)/%.dnx: $(INTERMDIR)/%.prg $(DOYNAX_LZ) + $(DOYNAX_LZ) -o $@ $< + $(CONV) dnx $< $@ $@ + +$(INTERMDIR)/%.exo: $(INTERMDIR)/%.prg $(EXO) + $(EXO) mem -f $< -o $@ + +$(INTERMDIR)/%.lc: $(INTERMDIR)/%.prg $(LC) + $(LC) $(LCSPEED) $< $@ + $(CONV) lc $< $@ $@ + +$(INTERMDIR)/%.lzsa2: $(INTERMDIR)/%.prg $(LZSA2) + $(LZSA2) -f 2 $< $@ + +$(INTERMDIR)/%.nc: $(INTERMDIR)/%.prg $(NC) + $(NC) $< --auto -o $@ + +$(INTERMDIR)/%.pu: $(INTERMDIR)/%.prg $(PU) + $(PU) -c0 -x 0 $< $@ + +$(INTERMDIR)/%.ssz: $(INTERMDIR)/%.prg $(SUBSIZER) + $(SUBSIZER) -m -f -o $@ $< + +$(INTERMDIR)/%.tc: $(INTERMDIR)/%.prg + $(TC) --inPlace $< $@ + +$(INTERMDIR)/%.ts: $(INTERMDIR)/%.prg $(TS_ENCODER) + $(TS) -i $< $@ + +$(INTERMDIR)/%.zx0: $(INTERMDIR)/%.prg $(ZX0) + $(ZX0) -o $@ $< + +$(BITNAX): + $(CC) $@.c -o $@ + +$(B2): + $(MAKE) -C $(B2_SOURCE) + +$(DOYNAX_LZ): + $(CC) $@.c -o $@ + +$(EXO): + $(MAKE) -C $(EXO_SOURCE) + +$(LC): + $(MAKE) -C $(LC_SOURCE) + +$(LZSA2): + $(MAKE) -C $(LZSA2_SOURCE) + +$(NC): + $(MAKE) -C $(NC_SOURCE) + +$(PU): + $(MAKE) -C $(PU_SOURCE) + +$(SUBSIZER): + $(MAKE) -C $(SUBSIZER_SRC) + +$(TS): + $(BUILD_TS) + +$(ZX0): + $(MAKE) -C $(ZX0_SRC) diff --git a/loader/samples/benchmark/benchmark-bitfire.mon b/loader/samples/benchmark/benchmark-bitfire.mon new file mode 100644 index 0000000..bdb9b8a --- /dev/null +++ b/loader/samples/benchmark/benchmark-bitfire.mon @@ -0,0 +1,2 @@ +watch store d7fe +command 1 "log on;m fd ff;log off;x" diff --git a/loader/samples/benchmark/benchmark-spindle.mon b/loader/samples/benchmark/benchmark-spindle.mon new file mode 100644 index 0000000..f75441c --- /dev/null +++ b/loader/samples/benchmark/benchmark-spindle.mon @@ -0,0 +1,2 @@ +watch store d7fe +command 1 "log on;m 0428 0428;m 0429 0438;log off;x" diff --git a/loader/samples/benchmark/benchmark.s b/loader/samples/benchmark/benchmark.s new file mode 100644 index 0000000..7efa801 --- /dev/null +++ b/loader/samples/benchmark/benchmark.s @@ -0,0 +1,310 @@ + +; all measurements are done with video frame granularity on PAL + +.ifndef LOADERCPU +LOADERCPU = 100; per cent +.endif + +OVERALL = (BENCHMARK = BITFIRE) + +.enum + BITFIRE = 0; throughput is 185526 * 50 / = X B/s + SPINDLE_CODE = 1; throughput is $4000 * 50 / = X B/s + SPINDLE_GRAPHICS = 2; throughput is $4400 * 50 / = X B/s +.endenum + +VERIFY = 0 +NONSEQUENTIAL = 0 + +FILECOUNT = $fc +LOADER_CPU = $fd +FRAMECOUNT = $fe + +.include "loadersymbols-c64.inc" + +.ifdef loadcompd +LOAD = loadcompd +.else +LOAD = loadraw +.endif + + .org $080d + + lda #$7f + sta $dc0d + + jsr install + + ldx #test + jsr LOAD + bcc :+ + +error: inc $d021 + sta $d021 + jmp error + +: lda #$35 + sta $01 + + lda numfiles + sta FILECOUNT + lda #LOADERCPU + sta LOADER_CPU + sta $0428 + lda #0 + ldx #38 +: sta $0429,x + dex + bpl :- + +.if LOADERCPU < 100 + + lda #irq + sta $ffff + + lda #$1b + sta $d011 + lda #$33 + sta $d012 + + lda #$01 + sta $d01a + sta $d019 + +.endif + lda #<((312 * 63) - 1); PAL + sta $dc04 + lda #>((312 * 63) - 1); PAL + sta $dc05 + lda #$ff + sta $dc06 + sta $dc07 +.if OVERALL +.else +loadnext: +.endif + lda #$51 + sta $dc0f + sta $dc0e +.if OVERALL +loadnext: +.endif + +.if NONSEQUENTIAL + lda filenames + inc * - 2 + sta filename + ldx #filename +.else + ldx #next +.endif + jsr LOAD +.if LOADERCPU < 100 + bcs error +.endif +.if OVERALL +.else + ;lda #0 + sta $dc0e + ldx #0 + inc * - 1 + sec + lda #$ff + sbc $dc06 + sta $0429,x + lda #$ff + sbc $dc07 + sta $0429 + 8,x +.endif +.if VERIFY + jsr checksum +.endif + dec FILECOUNT + bne loadnext + +.if OVERALL + ;lda #0 + sta $dc0e + sec + lda #$ff + sbc $dc06 + tay + lda #$ff + sbc $dc07 + tax + tya +.else + clc + lda #0 + tax + ldy numfiles + dey +: adc $0429,y + bcc :+ + inx + clc +: dey + bpl :-- +.endif + sta FRAMECOUNT + 0 + stx FRAMECOUNT + 1 + + jsr prntfrmcnt + + lda #0 + sta $d7fe + sta $d7ff + +done: inc $d020 + jmp done + +prntfrmcnt: ldx #1 + ldy #0 + jsr :+ + dex +: lda FRAMECOUNT,x + pha + lsr + lsr + lsr + lsr + jsr :+ + pla + and #$0f +: clc + adc #'0' + cmp #'9' + 1 + bcc :+ + sbc #'9' +: sta $0400,y + iny + rts + +.if LOADERCPU < 100 + +irq: pha + txa + pha + tya + pha + + lda $d020 + pha + ldx #<(312 * (100 - LOADERCPU) / 100); PAL + ldy #>(312 * (100 - LOADERCPU) / 100); PAL +: lda $d012 +: inc $d020 + cmp $d012 + beq :- + dex + bne :-- + dey + bpl :-- + pla + sta $d020 + + pla + tay + pla + tax + pla + inc $d019 + rti + +.endif + +numfiles: +.if BENCHMARK = BITFIRE + .byte 18 +.else + .byte 8 +.endif +test: .asciiz "test" +.if NONSEQUENTIAL +filename: .asciiz " .tc" +filenames: .byte "abcdefghijklmnopqr" +.else +next = * - 1 +.endif + +.if VERIFY + +OFFSET = 0; $e9 + +checksum: ldy #0 + lda verify,y + sec + sbc #OFFSET + sta checkloop + 2 + sec + lda verify + 2,y + sbc verify,y + tax + lda verify + 3,y + sbc verify + 1,y + tay + clc + txa + eor #$ff + adc #1 + tax + tya + eor #$ff + adc #0 + tay + clc + lda #0 +checkloop: adc $ff00 + inc checkloop + 1 + bne :+ + inc checkloop + 2 +: inx + bne checkloop + iny + bne checkloop + ldy checksum + 1 + ldx verify + 4,y + cmp verify + 4,y + bne chksumfail + tya + clc + adc #6 + sta checksum + 1 + rts + +chksumfail: .byte 2 + +verify: .word $a800, $b4f2, $f4; a + .word $2800, $bf80, $24; b + .word $7400, $bd00, $af; c + .word $2f80, $4900, $8d; d + .word $2800, $6600, $9d; e + .word $2800, $4396, $69; f + .word $5c00, $62d5, $7e; g + .word $2800, $2d00, $e9; h + .word $2900, $4500, $50; i + .word $4800, $6358, $b0; j + .word $2800, $6200, $39; k + .word $6100, $7300, $45; l + .word $2800, $67a1, $9c; m + .word $b900, $bef7, $f8; n + .word $6600, $8000, $a3; o + .word $9000, $af00, $d4; p + .word $2800, $666b, $b8; q + .word $8000, $a800, $58; r + +.endif; VERIFY + + .res LOAD - * +.incbin "../../build/loader-c64.prg", 2 + + .res install - * +.incbin "../../build/install-c64.prg", 2 diff --git a/loader/samples/benchmark/loaderconfig.inc b/loader/samples/benchmark/loaderconfig.inc new file mode 100644 index 0000000..0c2d700 --- /dev/null +++ b/loader/samples/benchmark/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 1 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 0 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 1 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/benchmark/make-tex-graph-bitfire.lua b/loader/samples/benchmark/make-tex-graph-bitfire.lua new file mode 100755 index 0000000..4fb1031 --- /dev/null +++ b/loader/samples/benchmark/make-tex-graph-bitfire.lua @@ -0,0 +1,12 @@ +#! /usr/bin/env lua + +for line in io.stdin:lines() do + local loader_cpu, frames_lo, frames_hi = line:match("^%S+%s*(%x+)%s*(%x+)%s*(%x+)") + + loader_cpu = tonumber("0x"..loader_cpu) + frames = tonumber("0x"..frames_lo) + (256 * tonumber("0x"..frames_hi)) + + throughput = 185526 * 50 / frames; + + print(("(%d, %.f)"):format(loader_cpu, throughput)) +end diff --git a/loader/samples/benchmark/make-tex-graph-spindle-code.lua b/loader/samples/benchmark/make-tex-graph-spindle-code.lua new file mode 100755 index 0000000..8699a0b --- /dev/null +++ b/loader/samples/benchmark/make-tex-graph-spindle-code.lua @@ -0,0 +1,35 @@ +#! /usr/bin/env lua + +local lines = {} + +for line in io.stdin:lines() do + lines[#lines + 1] = line +end + +local loader_cpu + +for k, v in pairs(lines) do + if k % 2 == 1 then + loader_cpu = v:match("^%S+%s*(%x+)") + else + local f1l,f2l,f3l,f4l,f5l,f6l,f7l,f8l, f1h,f2h,f3h,f4h,f5h,f6h,f7h,f8h = v:match("^%S+%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)") + + loader_cpu = tonumber("0x"..loader_cpu) + + local frames = {} + frames[1] = tonumber("0x"..f1l) + (256 * tonumber("0x"..f1h)) + frames[2] = tonumber("0x"..f2l) + (256 * tonumber("0x"..f2h)) + frames[3] = tonumber("0x"..f3l) + (256 * tonumber("0x"..f3h)) + frames[4] = tonumber("0x"..f4l) + (256 * tonumber("0x"..f4h)) + frames[5] = tonumber("0x"..f5l) + (256 * tonumber("0x"..f5h)) + frames[6] = tonumber("0x"..f6l) + (256 * tonumber("0x"..f6h)) + frames[7] = tonumber("0x"..f7l) + (256 * tonumber("0x"..f7h)) + frames[8] = tonumber("0x"..f8l) + (256 * tonumber("0x"..f8h)) + + table.sort(frames) + + throughput = 0x4000 * 50 / ((frames[4] + frames[5]) / 2); + + print(("(%d, %.f)"):format(loader_cpu, throughput)) + end +end diff --git a/loader/samples/benchmark/make-tex-graph-spindle-graphics.lua b/loader/samples/benchmark/make-tex-graph-spindle-graphics.lua new file mode 100755 index 0000000..f053683 --- /dev/null +++ b/loader/samples/benchmark/make-tex-graph-spindle-graphics.lua @@ -0,0 +1,35 @@ +#! /usr/bin/env lua + +local lines = {} + +for line in io.stdin:lines() do + lines[#lines + 1] = line +end + +local loader_cpu + +for k, v in pairs(lines) do + if k % 2 == 1 then + loader_cpu = v:match("^%S+%s*(%x+)") + else + local f1l,f2l,f3l,f4l,f5l,f6l,f7l,f8l, f1h,f2h,f3h,f4h,f5h,f6h,f7h,f8h = v:match("^%S+%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)%s*(%x+)") + + loader_cpu = tonumber("0x"..loader_cpu) + + local frames = {} + frames[1] = tonumber("0x"..f1l) + (256 * tonumber("0x"..f1h)) + frames[2] = tonumber("0x"..f2l) + (256 * tonumber("0x"..f2h)) + frames[3] = tonumber("0x"..f3l) + (256 * tonumber("0x"..f3h)) + frames[4] = tonumber("0x"..f4l) + (256 * tonumber("0x"..f4h)) + frames[5] = tonumber("0x"..f5l) + (256 * tonumber("0x"..f5h)) + frames[6] = tonumber("0x"..f6l) + (256 * tonumber("0x"..f6h)) + frames[7] = tonumber("0x"..f7l) + (256 * tonumber("0x"..f7h)) + frames[8] = tonumber("0x"..f8l) + (256 * tonumber("0x"..f8h)) + + table.sort(frames) + + throughput = 0x4400 * 50 / ((frames[4] + frames[5]) / 2); + + print(("(%d, %.f)"):format(loader_cpu, throughput)) + end +end diff --git a/loader/samples/cc65/Linkfile b/loader/samples/cc65/Linkfile new file mode 100644 index 0000000..87d083a --- /dev/null +++ b/loader/samples/cc65/Linkfile @@ -0,0 +1,50 @@ +FEATURES { + STARTADDRESS: default = $0801; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __HIMEM__: type = weak, value = $D000; +} +MEMORY { + ZP: file = "", define = yes, start = $0002, size = $001F; + ZP2: file = "", define = yes, start = $00FB, size = $0005; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $000D; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__; + BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + + DISKIO_ZP: load = ZP, type = zp; + DISKIO: load = MAIN, type = ro; + DISKIO_INSTALL: load = MAIN, type = ro; + + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; + ONCE: load = MAIN, type = ro, define = yes; + BSS: load = BSS, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/loader/samples/cc65/Makefile b/loader/samples/cc65/Makefile new file mode 100644 index 0000000..4b4437a --- /dev/null +++ b/loader/samples/cc65/Makefile @@ -0,0 +1,46 @@ + +NAME = c-program +_PLATFORM_ = c64 + +EMU = x64sc +C1541 = c1541 + +PRINTF = printf + +CAT = cat + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADERSRC = ../../src +LOADER = $(BUILDDIR)/loader-$(_PLATFORM_).lib + +COMPILE = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d64 + +RESOURCESDIR = ../resources +PIC1 = $(INTERMDIR)/pic1.prg +PIC2 = $(INTERMDIR)/pic2.prg + +SOURCE = $(NAME).c + +$(LOADER): + $(MAKE) -C $(LOADERSRC) EXTCONFIGPATH=../samples/cc65 lib + +$(COMPILE): $(SOURCE) $(LOADER) + cl65 -t $(_PLATFORM_) -C ./Linkfile -o $@ $^ + +$(DISKIMAGE): $(COMPILE) $(PIC1) $(PIC2) + $(C1541) -format "normal is boring,+h" d64 $@ + $(C1541) -attach $@ \ + -write $(COMPILE) "$(NAME)" \ + -write $(PIC1) "pic1" \ + -write $(PIC2) "pic2" + +$(INTERMDIR)/%.prg: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + +run: $(DISKIMAGE) + $(EMU) $(realpath $^) + +clean: + -$(RM) *.o $(PIC1) $(PIC2) $(COMPILE) $(DISKIMAGE) $(LOADER) diff --git a/loader/samples/cc65/c-program.c b/loader/samples/cc65/c-program.c new file mode 100644 index 0000000..46db194 --- /dev/null +++ b/loader/samples/cc65/c-program.c @@ -0,0 +1,98 @@ + +#include <6502.h> +#include +#include + +static unsigned char irq_stack[64]; + +static void *COLOURS = (void *) 0x5c00; +static void *BITMAP = (void *) 0x6000; + +static char status; + +static void +install_loader(void) +{ + asm("jsr install"); +} + +static int +load_file(const char *filename) +{ + static char lo; + static char hi; + + lo = (int) filename; + hi = ((int) filename) >> 8; + + asm("ldx %v\n" /* lo */ + "ldy %v\n" /* hi */ + "jsr loadraw\n" + "bcs err\n" + "lda #0\n" + "err:\n" + "sta %v\n" /* status */ + , lo, hi, status); + + return status; +} + +static unsigned char +handle_irq(void) +{ + ++VIC.bordercolor; + --VIC.bordercolor; + + VIC.irr = 1; + + return IRQ_HANDLED; +} + +static void +init(void) +{ + memset(COLOURS, 0xfb, 25 * 40); + memset(BITMAP, 0, 25 * 40 * 8); + + CIA1.icr = 0x7f; + + CIA2.pra = 2; + + set_irq(&handle_irq, irq_stack, sizeof irq_stack); + + VIC.ctrl1 = 0x3b; + VIC.addr = 0x78; + VIC.rasterline = 0x20; + + VIC.irr = 1; + VIC.imr = 1; +} + +static void +error(void) +{ + do { + ++VIC.bordercolor; + VIC.bordercolor = status; + } while (1); +} + +void +main(void) +{ + install_loader(); + + init(); + + do { + if (load_file("pic1") != 0) { + error(); + } + ++VIC.bordercolor; + + if (load_file("pic2") != 0) { + error(); + } + ++VIC.bordercolor; + } while (1); +} diff --git a/loader/samples/cc65/loaderconfig.inc b/loader/samples/cc65/loaderconfig.inc new file mode 100644 index 0000000..54be40c --- /dev/null +++ b/loader/samples/cc65/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 1 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/drivecode/Makefile b/loader/samples/drivecode/Makefile new file mode 100644 index 0000000..5dad8f9 --- /dev/null +++ b/loader/samples/drivecode/Makefile @@ -0,0 +1,68 @@ + +NAME = drivecode +_PLATFORM_ = c64 + +VICE = x64sc +EMU = $(VICE) -drive8type 1541 -drive9type 0 -autostart +EMU71 = $(VICE) -drive8type 1571 -drive9type 0 -autostart +EMU81 = $(VICE) -drive8type 1581 -drive9type 0 -autostart + +AS = cl65 +AS_FLAGS = -Wa -I../../build -Wa -I../../include -u __EXEHDR__ + +CC1541 = ../../tools/cc1541/cc1541 -v +CC1541_SOURCE = ../../tools/cc1541 + +PRINTF = printf + +RM = rm -f +CP = cp +MV = mv +CAT = cat + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg + +RESOURCESDIR = ../resources +TEST = $(RESOURCESDIR)/test.prg +PIC1 = $(INTERMDIR)/pic1.prg + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d64 +DISKIMAGE81 = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d81 + +$(DISKIMAGE) $(DISKIMAGE81): $(ASSEMBLE) $(PIC1) $(CC1541) + $(RM) $@ + $(CC1541) -n "normal is boring" -i plush \ + -f $(NAME) -w $< \ + -f "pic1" -w $(PIC1) \ + $@ + +$(ASSEMBLE): $(SOURCE) $(LOADER) + $(AS) $(AS_FLAGS) -C c64-asm.cfg -Wa -DPLATFORM=64 -o $@ $< + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) PLATFORM=$(_PLATFORM_) PROJECT=$(NAME) INSTALL=0d00 RESIDENT=0900 ZP=04 TRANSIENT=0a00 ZP=04 customdrivecode + +$(INTERMDIR)/%.prg: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + +run: $(DISKIMAGE) + $(EMU) $(realpath $^) + +run71: $(DISKIMAGE) + $(EMU71) $(realpath $^) + +run81: $(DISKIMAGE81) + $(EMU81) $(realpath $^) + +clean: + -$(RM) *.o $(PIC1) $(ASSEMBLE) $(DISKIMAGE) $(BUILDDIR)/loadersymbols-c64.prg $(BUILDDIR)/install-c64.prg $(BUILDDIR)/loader-c64.prg $(BUILDDIR)/customdrivecode-c64.prg + + +$(CC1541): $(CC1541_SOURCE)/cc1541.c + $(MAKE) -C $(CC1541_SOURCE) cc1541 diff --git a/loader/samples/drivecode/drivecode.s b/loader/samples/drivecode/drivecode.s new file mode 100644 index 0000000..951e038 --- /dev/null +++ b/loader/samples/drivecode/drivecode.s @@ -0,0 +1,77 @@ + +.include "loadersymbols-c64.inc" + +.include "loader.inc" + + .org $080d + + jsr install + +loop: inc $d020 + + ldx #swapparams + jsr swapdrvcod + + inc $d020 + +wait: bit $dd00 + bpl wait + + inc $d020 + + jsr restoreldr + + inc $d020 + + ldx #filename + jsr loadraw + bcc loop + +error: sta $d021 + inc $d021 + jmp error + +swapparams: .word $8000; buffer + .word drvcoderun; entry + .word drvcoderun; to + .word drivecoded - drivecode; length + .word drivecode; from + +filename: .byte "pic1", 0 + +drivecode: + .org $0200 + +drvcoderun: lda #$12; ATNA_OUT | DATA_OUT + sta $1800; busy + +drvcodloop: lda #$08 + eor $1c00 + sta $1c00 + lda #0 +: dex + bne :- +: dey + bne :- + clc + adc #1 + bne :-- + lda #6 + dec * - 1 + bne drvcodloop + + RETURNTOLOADER41 + + .org * - (drvcoderun - drivecode) +drivecoded: + + .res loadraw - * +.incbin "../../build/loader-c64.prg", 2 + + .res swapdrvcod - * +.incbin "../../build/customdrivecode-c64.prg", 2 + + .res install - * +.incbin "../../build/install-c64.prg", 2 diff --git a/loader/samples/drivecode/loaderconfig.inc b/loader/samples/drivecode/loaderconfig.inc new file mode 100644 index 0000000..fe62757 --- /dev/null +++ b/loader/samples/drivecode/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 1 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/minexample/Makefile b/loader/samples/minexample/Makefile new file mode 100644 index 0000000..ac39e84 --- /dev/null +++ b/loader/samples/minexample/Makefile @@ -0,0 +1,194 @@ + +ifeq ($(PLATFORM),) +_PLATFORM_ = c64 +else ifeq ($(PLATFORM),c116) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),plus4) +_PLATFORM_ = c16 +else +_PLATFORM_ = $(PLATFORM) +endif + +ifeq ($(NO_VICE),) +NO_VICE = 0 +endif + + +ifneq ($(_PLATFORM_),c64) +ifneq ($(_PLATFORM_),c128) +ifneq ($(_PLATFORM_),c16) +$(error invalid platform $(_PLATFORM_) specified) +endif +endif +endif + + +ARCH = $(shell uname | tr "[a-z]" "[A-Z]" | tr -c -d "[A-Z]") + +ifneq ($(findstring CYGWINNT,$(ARCH)),) + ifeq (CYGWINNT,$(ARCH)) +ARCH = WIN32 + else +ARCH = WIN64 + endif +endif +ifneq ($(findstring DARWIN,$(ARCH)),) +ARCH = MACOSX +endif + + +ifeq ($(_PLATFORM_),c16) + ifneq ($(NO_VICE),0) + ifneq ($(findstring WIN,$(ARCH)),) +USE_PLUS4EMU = 0 +USE_YAPE = 1 + else +USE_PLUS4EMU = 1 +USE_YAPE = 0 + endif + else +USE_PLUS4EMU = 0 +USE_YAPE = 0 + endif +else +USE_PLUS4EMU = 0 +USE_YAPE = 0 +endif + + +ifeq ($(_PLATFORM_),c16) + ifeq ($(ARCH),MACOSX) + # MacOSX, these programs must be installed as applications +VICE = xplus4 +PLUS4EMU = open /Applications/plus4emu.app --args + else +VICE = xplus4 +PLUS4EMU = plus4emu + ifeq ($(ARCH),WIN64) +YAPE = YapeWin64 + else +YAPE = Yape + endif + endif +else + ifeq ($(_PLATFORM_),c128) +VICE = x128 +USE_VICE = 1 + else +VICE = x64 +USE_VICE = 1 + endif +endif + +ifeq ($(USE_VICE),1) +EMU = $(VICE) -drive8type 1541 -drive9type 0 -autostart +EMU71 = $(VICE) -drive8type 1571 -drive9type 0 -autostart +else +EMU = $(PLUS4EMU) -disk +endif + + +ECHO = echo +PRINTF = printf + +AS = cl65 +LD = ld65 +C1541 = c1541 +CC1541 = ../../tools/cc1541/cc1541 + +MKDIR = mkdir -p +RM = rm -f +ifeq ($(ARCH),MACOSX) +RMDIR = rmdir # XXX TODO xargs to remove .DS_Store +else +RMDIR = rmdir +endif +CAT = cat + + +.PHONY: default loader assemble diskimage run clean distclean wipe +.PHONY: tellarch + + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/loader-$(_PLATFORM_).prg + +RESOURCESDIR = ../resources + +NAME = minexample + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d64 + +AS_FLAGS = -Wa -I../../../shared -I ../../include -u __EXEHDR__ + + +default: diskimage + + +tellarch: + @$(ECHO) $(ARCH) + + +loader: $(LOADER) + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) PLATFORM=$(_PLATFORM_) INSTALL=1800 RESIDENT=1700 ZP=02 prg + + +assemble: $(ASSEMBLE) + +$(ASSEMBLE): $(SOURCE) $(LOADER) $(LOADERCFG) + $(MKDIR) $(BUILDDIR) + $(MKDIR) $(INTERMDIR) +ifeq ($(_PLATFORM_),c64) + $(AS) $(AS_FLAGS) -C c64-asm.cfg -Wa -DPLATFORM=64 -o $@ $< +else ifeq ($(_PLATFORM_),c128) + $(AS) $(AS_FLAGS) -t c128 -Wa -DPLATFORM=128 -o $@ $< +else + $(AS) $(AS_FLAGS) -t c16 -Wa -DPLATFORM=16 -o $@ $< +endif + + +diskimage: $(DISKIMAGE) + +$(DISKIMAGE): $(ASSEMBLE) + $(RM) $@ + $(CC1541) -n "ys2 intro" -i test \ + -f $(NAME) -w $< \ + -f "sid" -w sid.bin \ + -f "badguy" -w badguy.zx0.prg \ + $@ + + +ifneq ($(USE_YAPE),0) +run: $(DISKIMAGE) + $(YAPE) "..\..\build\$^" +else +run: $(DISKIMAGE) + $(EMU) $(realpath $^) + +run71: $(DISKIMAGE) + $(EMU71) $(realpath $^) +endif + +$(INTERMDIR)/%.prg: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + + +clean: + -$(RM) *.o $(ASSEMBLE) $(DISKIMAGE) + -$(RM) -rf $(INTERMDIR) + -$(RM) $(BUILDDIR)/loader-c64.prg + -$(RM) $(BUILDDIR)/loader-c128.prg + -$(RM) $(BUILDDIR)/loader-c16.prg + -$(RMDIR) $(BUILDDIR) + +distclean: + -$(MAKE) -C $(LOADER_SRC) clean + +wipe: distclean clean diff --git a/loader/samples/minexample/anim2cus.py b/loader/samples/minexample/anim2cus.py new file mode 100644 index 0000000..3cd657a --- /dev/null +++ b/loader/samples/minexample/anim2cus.py @@ -0,0 +1,77 @@ +import sys +f = list(open(sys.argv[1], "rb").read())[2:] + +out = f[:(0x4711-0x2000)] + +a = open(sys.argv[2],"r").read().split("\n") +a = [x for x in a if x != ""] +a = [x.split(",")[1] for x in a[:-1]] + +frame = [] +frame_dict = {} + +cnt = 0 +def append_frame(frame): + global cnt + if cnt < len(a): + filename_csv = a[cnt] + if filename_csv not in frame_dict: + frame_dict[filename_csv] = [] + for d in frame: + if d not in frame_dict[filename_csv]: + frame_dict[filename_csv].append(d) + else: + print(cnt, len(a)) + cnt += 1 + +ptr = 0x4711-0x2000 +while ptr < len(f): + num_chars = f[ptr] + #print(ptr,len(f),num_chars) + ptr += 1 + if num_chars == 0: + delay = f[ptr] + ptr += 1 + append_frame(frame) + frame = [] + continue + elif num_chars == 0xFF: + append_frame(frame) + break + bitmap = (f[ptr]|(f[ptr+1]<<8))+0x2000 + ptr += 2 + screen = (f[ptr]|(f[ptr+1]<<8))+0x400 + cram = (f[ptr]|(f[ptr+1]<<8))+0xd800 + ptr += 2 + for i in range(num_chars): + frame.append([bitmap,f[ptr:ptr+8]]) + ptr += 8 + frame.append([screen,f[ptr]]) + frame.append([cram,f[ptr+1]]) + ptr += 2 + bitmap += 8 + screen += 1 + cram += 1 + +out_pat_addr = len(out) +out.extend([0]*(len(a)*3)) + +frame_dict_offs = [] +for i in frame_dict: + frame_dict_offs.append(len(out)) + sort_dict = sorted([x for x in frame_dict[i]], key=lambda x : x[0]) + print(sort_dict) + for j in sort_dict: + out.append(j[0]&0xff) + out.append(j[0]>>8&0xff) + if type(j[1]) == list: + out.extend(j[1]) + else: + out.append(j[1]) + +#print(out) + +print(hex(0x2000+len(out))) +f = open(sys.argv[3],"wb") +f.write(bytearray(out)) +f.close() \ No newline at end of file diff --git a/loader/samples/minexample/badguy.bin b/loader/samples/minexample/badguy.bin new file mode 100644 index 0000000..e28c2d4 Binary files /dev/null and b/loader/samples/minexample/badguy.bin differ diff --git a/loader/samples/minexample/badguy.prg b/loader/samples/minexample/badguy.prg new file mode 100644 index 0000000..e28c2d4 Binary files /dev/null and b/loader/samples/minexample/badguy.prg differ diff --git a/loader/samples/minexample/badguy.zx0 b/loader/samples/minexample/badguy.zx0 new file mode 100644 index 0000000..fa8a495 Binary files /dev/null and b/loader/samples/minexample/badguy.zx0 differ diff --git a/loader/samples/minexample/badguy.zx0.prg b/loader/samples/minexample/badguy.zx0.prg new file mode 100644 index 0000000..986fef8 Binary files /dev/null and b/loader/samples/minexample/badguy.zx0.prg differ diff --git a/loader/samples/minexample/badguy_bi.bin b/loader/samples/minexample/badguy_bi.bin new file mode 100644 index 0000000..667f656 Binary files /dev/null and b/loader/samples/minexample/badguy_bi.bin differ diff --git a/loader/samples/minexample/badguy_no_prg.bin b/loader/samples/minexample/badguy_no_prg.bin new file mode 100644 index 0000000..0cdbccf Binary files /dev/null and b/loader/samples/minexample/badguy_no_prg.bin differ diff --git a/loader/samples/minexample/badguys_new.bin b/loader/samples/minexample/badguys_new.bin new file mode 100644 index 0000000..cbd8446 Binary files /dev/null and b/loader/samples/minexample/badguys_new.bin differ diff --git a/loader/samples/minexample/badguys_new.bin.bak b/loader/samples/minexample/badguys_new.bin.bak new file mode 100755 index 0000000..179ca32 Binary files /dev/null and b/loader/samples/minexample/badguys_new.bin.bak differ diff --git a/loader/samples/minexample/c1541 b/loader/samples/minexample/c1541 new file mode 100755 index 0000000..dd41748 --- /dev/null +++ b/loader/samples/minexample/c1541 @@ -0,0 +1,4 @@ +#!/bin/bash +export VICE_INITIAL_CWD="$(pwd)" +export PROGRAM="c1541" +"/Applications/vice-arm64-gtk3-3.9/VICE.app/Contents/Resources/script" "$@" diff --git a/loader/samples/minexample/conv_zx0.sh b/loader/samples/minexample/conv_zx0.sh new file mode 100644 index 0000000..2962ca4 --- /dev/null +++ b/loader/samples/minexample/conv_zx0.sh @@ -0,0 +1,3 @@ +zx02/zx02 badguys_new.bin badguy.zx0 +printf "\x00\xe0" > badguy.zx0.prg +cat badguy.zx0 >> badguy.zx0.prg \ No newline at end of file diff --git a/loader/samples/minexample/libopencbm.dylib b/loader/samples/minexample/libopencbm.dylib new file mode 100644 index 0000000..02b2e29 Binary files /dev/null and b/loader/samples/minexample/libopencbm.dylib differ diff --git a/loader/samples/minexample/loaderconfig.inc b/loader/samples/minexample/loaderconfig.inc new file mode 100644 index 0000000..7901632 --- /dev/null +++ b/loader/samples/minexample/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists call for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/minexample/main.asm b/loader/samples/minexample/main.asm new file mode 100644 index 0000000..3718b33 --- /dev/null +++ b/loader/samples/minexample/main.asm @@ -0,0 +1,76 @@ +.feature c_comments + +.zeropage + +.segment "CODE" + +.org $080D +main: + sei + lda #$35 + sta $01 + + lda #127 + sta $dc0d + + and $d011 + sta $d011 + + lda $dc0d + lda $dd0d + + lda #irq + sta $ffff + + lda #$0b + sta $d011 + lda #$00 + sta $d012 + + lda #0 + sta $d01a + + lda #$63; <(985248/100) + sta $dc04 + lda #$26;>(985248/100) + sta $dc05 + + lda $dc0d + and #$81 + sta $dc0d + + lda #$40 + sta $dc0c + + lda #$81 + sta $dc0d + + lda #0 + jsr $1000 + cli + jmp * + +irq_music: + pha + txa + pha + tya + pha + + inc $d020 + jsr $1003 + dec $d020 + + pla + tay + pla + tax + pla + ;rti + jmp $dc0c + + +.res $1000-* +.incbin "ys2_sid.sid", $7e+($1000-$ff6) \ No newline at end of file diff --git a/loader/samples/minexample/minexample.o b/loader/samples/minexample/minexample.o new file mode 100644 index 0000000..131a0de Binary files /dev/null and b/loader/samples/minexample/minexample.o differ diff --git a/loader/samples/minexample/minexample.s b/loader/samples/minexample/minexample.s new file mode 100644 index 0000000..d7f6800 --- /dev/null +++ b/loader/samples/minexample/minexample.s @@ -0,0 +1,346 @@ +.feature c_comments +.include "../../build/loadersymbols-c64.inc" + +.ZEROPAGE +.org $10 +bmp_delay: .res 1 +bmp_addr: .res 2 +bmp_ptr: .res 2 +scr_ptr_lo: .res 1 +scr_ptr: .res 2 +cram_ptr: .res 2 +chr_count: .res 1 +vbl: .res 1 +frame: .res 1 +cur_frame: .res 1 +frame_delay: .res 1 +frame_until: .res 1 + +.segment "CODE" + +ZP=$e0 +offset = ZP+0 +ZX0_src = ZP+2 +ZX0_dst = ZP+4 +bitr = ZP+6 +pntr = ZP+7 + + + .org $080d + jsr install + + sei + lda #$35 + sta $01 + + lda #0 + sta ZX0_dst+0 + + ldx #badguy + jsr loadraw + + lda #0 + sta ZX0_src + lda #$e0 + sta ZX0_src+1 + lda #0 + sta ZX0_dst + lda #$20 + jsr zx02 + + lda #127 + sta $dc0d + + and $d011 + sta $d011 + + lda $dc0d + lda $dd0d + + lda #irq_badguy + sta $ffff + + lda #$1b + sta $d011 + lda #$80 + sta $d012 + + lda #1 + sta $d01a + sta bmp_delay + jsr init_bmp + + lda #0 + sta vbl + + cli + + lda #7 + sta frame_until + jsr wait_frame_until + + lda #50*5 + sta frame_delay + jsr wait_loop + + lda #$0d + sta frame_until + jsr wait_frame_until + + lda #50*5 + sta frame_delay + jsr wait_loop + + lda #50*5 + sta frame_delay + jsr wait_loop + + lda #26 + sta frame_until + jsr wait_frame_until + jmp * + +wait_loop: +loop2: + lda vbl + beq loop2 + + lda #0 + sta vbl + + jsr sfx_play + + dec frame_delay + lda frame_delay + beq :+ + jmp loop2 +: + rts + +wait_frame_until: +loop: + lda vbl + beq loop + + lda #0 + sta vbl + + lda cur_frame + cmp #2 + bcc :+ + jsr sfx_play +: + + dec bmp_delay + bpl :+ + jsr update_bmp +: + + lda cur_frame + cmp frame_until + beq :+ + + jmp loop +: + rts + +init_bmp: + lda #$3b + sta $d011 + lda #$19 + sta $d018 + lda #$d8 + sta $d016 + + lda $4710 + lsr + lsr + lsr + lsr + sta $d020 + + lda $4710 + and #$0f + sta $d021 + + lda #0 + sta cur_frame + sta bmp_delay + + lda #$11 + sta bmp_addr + lda #$47 + sta bmp_addr+1 + + ldx #0 +: + .repeat 4, I + lda $3f40+(I*250), x + sta $400+(I*250), x + lda $4328+(I*250), x + sta $d800+(I*250), x + .endrepeat + inx + cpx #250 + bne :- + rts + +inc_bmp_addr: + inc bmp_addr + bne :+ + inc bmp_addr+1 +: + rts + +update_bmp: + ldy #0 + sty scr_ptr + sty cram_ptr + lda (bmp_addr), y + jsr inc_bmp_addr + sta chr_count + cmp #0 ; tfw flags update in inc_bmp_addr + bne @skip_frame_end + lda (bmp_addr), y + jsr inc_bmp_addr + sta bmp_delay + inc cur_frame + lda cur_frame + cmp #2 + bne :+ + lda #0 + jsr sfx_init +: + rts +@skip_frame_end: + + lda (bmp_addr), y + sta bmp_ptr + iny + lda (bmp_addr), y + clc + adc #$20 + sta bmp_ptr+1 + + iny + lda (bmp_addr), y + sta scr_ptr_lo + iny + lda (bmp_addr), y + clc + adc #$04 + sta scr_ptr+1 + clc + adc #$d8-$04 + sta cram_ptr+1 + + ldy #0 + lda bmp_addr + clc + adc #4 + sta bmp_addr + bcc :+ + inc bmp_addr+1 +: + + ldx #0 +@loop: + ldy #0 + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + iny + lda (bmp_addr), y + sta (bmp_ptr), y + + lda bmp_ptr + clc + adc #8 + sta bmp_ptr + bcc :+ + inc bmp_ptr+1 +: + lda bmp_addr + clc + adc #8 + sta bmp_addr + bcc :+ + inc bmp_addr+1 +: + + ldy #0 + lda (bmp_addr), y + ldy scr_ptr_lo + sta (scr_ptr), y + + ldy #1 + lda (bmp_addr), y + ldy scr_ptr_lo + sta (cram_ptr), y + + inc scr_ptr_lo + bne :+ + inc scr_ptr+1 + inc cram_ptr+1 +: + + lda bmp_addr + clc + adc #2 + sta bmp_addr + bcc :+ + inc bmp_addr+1 +: + + dec chr_count + ldx chr_count + bne @loop + jmp update_bmp + +irq_badguy: + pha + txa + pha + tya + pha + + inc vbl + + asl $d019 + pla + tay + pla + tax + pla + rti + +badguy: .byte "badguy",0 + +.include "sfx.asm" +.include "zx02.asm" + + .res loadraw - * +.incbin "../../build/loader-c64.prg", 2 + + .res install - * +.incbin "../../build/install-c64.prg", 2 \ No newline at end of file diff --git a/loader/samples/minexample/png2prg 2/01_badguys.prg b/loader/samples/minexample/png2prg 2/01_badguys.prg new file mode 100644 index 0000000..0597cec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/01_badguys.prg differ diff --git a/loader/samples/minexample/png2prg 2/01_badguyz_cut.csv b/loader/samples/minexample/png2prg 2/01_badguyz_cut.csv new file mode 100644 index 0000000..3f403c0 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/01_badguyz_cut.csv @@ -0,0 +1,29 @@ +100,ys2/01_badguyz/koala/png/00.png +3,ys2/01_badguyz/koala/png/01.png +3,ys2/01_badguyz/koala/png/02.png +3,ys2/01_badguyz/koala/png/03.png +3,ys2/01_badguyz/koala/png/04.png +3,ys2/01_badguyz/koala/png/05.png +50,ys2/01_badguyz/koala/png/06.png +15,ys2/01_badguyz/koala/png/07.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/11.png +100,ys2/01_badguyz/koala/png/12.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/07.png +100,ys2/01_badguyz/koala/png/06.png +3,ys2/01_badguyz/koala/png/13.png +3,ys2/01_badguyz/koala/png/14.png +3,ys2/01_badguyz/koala/png/15.png +3,ys2/01_badguyz/koala/png/16.png +3,ys2/01_badguyz/koala/png/17.png +3,ys2/01_badguyz/koala/png/18.png +3,ys2/01_badguyz/koala/png/19.png +100,ys2/01_badguyz/koala/png/00.png + + + diff --git a/loader/samples/minexample/png2prg 2/03_tower.csv b/loader/samples/minexample/png2prg 2/03_tower.csv new file mode 100644 index 0000000..9e5eb6c --- /dev/null +++ b/loader/samples/minexample/png2prg 2/03_tower.csv @@ -0,0 +1,20 @@ +100,ys2/03_tower/koala/png/01.png +3,ys2/03_tower/koala/png/02.png +3,ys2/03_tower/koala/png/03.png +3,ys2/03_tower/koala/png/04.png +3,ys2/03_tower/koala/png/05.png +3,ys2/03_tower/koala/png/06.png +3,ys2/03_tower/koala/png/03.png +50,ys2/03_tower/koala/png/01.png +10,ys2/03_tower/koala/png/07.png +10,ys2/03_tower/koala/png/08.png +10,ys2/03_tower/koala/png/09.png +10,ys2/03_tower/koala/png/10.png +10,ys2/03_tower/koala/png/11.png +10,ys2/03_tower/koala/png/12.png +10,ys2/03_tower/koala/png/13.png +10,ys2/03_tower/koala/png/14.png +150,ys2/03_tower/koala/png/15.png + + + diff --git a/loader/samples/minexample/png2prg 2/03_tower.prg b/loader/samples/minexample/png2prg 2/03_tower.prg new file mode 100644 index 0000000..c75b4e3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/03_tower.prg differ diff --git a/loader/samples/minexample/png2prg 2/04_tower_beam.csv b/loader/samples/minexample/png2prg 2/04_tower_beam.csv new file mode 100644 index 0000000..a921a92 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/04_tower_beam.csv @@ -0,0 +1,40 @@ +50,ys2/04_tower_beam/koala/png/01.png +5,ys2/04_tower_beam/koala/png/02.png +5,ys2/04_tower_beam/koala/png/03.png +3,ys2/04_tower_beam/koala/png/04.png +3,ys2/04_tower_beam/koala/png/05.png +3,ys2/04_tower_beam/koala/png/06.png +3,ys2/04_tower_beam/koala/png/07.png +3,ys2/04_tower_beam/koala/png/08.png +3,ys2/04_tower_beam/koala/png/09.png +3,ys2/04_tower_beam/koala/png/10.png +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + +3,ys2/04_tower_beam/koala/png/11.png +3,ys2/04_tower_beam/koala/png/12.png +3,ys2/04_tower_beam/koala/png/13.png + + + diff --git a/loader/samples/minexample/png2prg 2/04_tower_beam.prg b/loader/samples/minexample/png2prg 2/04_tower_beam.prg new file mode 100644 index 0000000..482f27f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/04_tower_beam.prg differ diff --git a/loader/samples/minexample/png2prg 2/05_field.csv b/loader/samples/minexample/png2prg 2/05_field.csv new file mode 100644 index 0000000..eede219 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/05_field.csv @@ -0,0 +1,69 @@ +3,ys2/05_field/koala/png/01.png +3,ys2/05_field/koala/png/02.png +3,ys2/05_field/koala/png/03.png +3,ys2/05_field/koala/png/04.png +3,ys2/05_field/koala/png/05.png +3,ys2/05_field/koala/png/06.png +3,ys2/05_field/koala/png/07.png +3,ys2/05_field/koala/png/08.png + +3,ys2/05_field/koala/png/01.png +3,ys2/05_field/koala/png/02.png +3,ys2/05_field/koala/png/03.png +3,ys2/05_field/koala/png/04.png +3,ys2/05_field/koala/png/05.png +3,ys2/05_field/koala/png/06.png +3,ys2/05_field/koala/png/07.png +3,ys2/05_field/koala/png/08.png + +3,ys2/05_field/koala/png/01.png +3,ys2/05_field/koala/png/02.png +3,ys2/05_field/koala/png/03.png +3,ys2/05_field/koala/png/04.png +3,ys2/05_field/koala/png/05.png +3,ys2/05_field/koala/png/06.png +3,ys2/05_field/koala/png/07.png +3,ys2/05_field/koala/png/08.png + +3,ys2/05_field/koala/png/01.png +3,ys2/05_field/koala/png/02.png +3,ys2/05_field/koala/png/03.png +3,ys2/05_field/koala/png/04.png +3,ys2/05_field/koala/png/05.png +3,ys2/05_field/koala/png/06.png +3,ys2/05_field/koala/png/07.png +3,ys2/05_field/koala/png/08.png + +3,ys2/05_field/koala/png/01.png +3,ys2/05_field/koala/png/02.png +3,ys2/05_field/koala/png/03.png +3,ys2/05_field/koala/png/04.png +3,ys2/05_field/koala/png/05.png +3,ys2/05_field/koala/png/06.png +3,ys2/05_field/koala/png/07.png +3,ys2/05_field/koala/png/08.png + +3,ys2/05_field/koala/png/09.png +3,ys2/05_field/koala/png/10.png +3,ys2/05_field/koala/png/11.png +3,ys2/05_field/koala/png/12.png +3,ys2/05_field/koala/png/13.png +3,ys2/05_field/koala/png/14.png +3,ys2/05_field/koala/png/15.png +3,ys2/05_field/koala/png/16.png +3,ys2/05_field/koala/png/17.png +3,ys2/05_field/koala/png/18.png +3,ys2/05_field/koala/png/19.png +3,ys2/05_field/koala/png/20.png +3,ys2/05_field/koala/png/21.png +3,ys2/05_field/koala/png/22.png +3,ys2/05_field/koala/png/23.png +3,ys2/05_field/koala/png/24.png +3,ys2/05_field/koala/png/25.png +3,ys2/05_field/koala/png/26.png +3,ys2/05_field/koala/png/27.png +3,ys2/05_field/koala/png/28.png +3,ys2/05_field/koala/png/29.png +3,ys2/05_field/koala/png/30.png + + diff --git a/loader/samples/minexample/png2prg 2/05_field.prg b/loader/samples/minexample/png2prg 2/05_field.prg new file mode 100644 index 0000000..7cd08bc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/05_field.prg differ diff --git a/loader/samples/minexample/png2prg 2/06_falling_star.csv b/loader/samples/minexample/png2prg 2/06_falling_star.csv new file mode 100644 index 0000000..dd6134b --- /dev/null +++ b/loader/samples/minexample/png2prg 2/06_falling_star.csv @@ -0,0 +1,38 @@ +50,ys2/06_falling_star/koala/png/01.png +5,ys2/06_falling_star/koala/png/02.png +5,ys2/06_falling_star/koala/png/03.png +5,ys2/06_falling_star/koala/png/04.png +5,ys2/06_falling_star/koala/png/05.png +5,ys2/06_falling_star/koala/png/06.png +5,ys2/06_falling_star/koala/png/07.png +5,ys2/06_falling_star/koala/png/08.png +5,ys2/06_falling_star/koala/png/09.png +5,ys2/06_falling_star/koala/png/10.png +5,ys2/06_falling_star/koala/png/11.png +5,ys2/06_falling_star/koala/png/12.png +5,ys2/06_falling_star/koala/png/13.png +5,ys2/06_falling_star/koala/png/14.png +5,ys2/06_falling_star/koala/png/15.png +5,ys2/06_falling_star/koala/png/16.png +5,ys2/06_falling_star/koala/png/17.png +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + +5,ys2/06_falling_star/koala/png/18.png +5,ys2/06_falling_star/koala/png/19.png + diff --git a/loader/samples/minexample/png2prg 2/06_falling_star.prg b/loader/samples/minexample/png2prg 2/06_falling_star.prg new file mode 100644 index 0000000..260c796 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/06_falling_star.prg differ diff --git a/loader/samples/minexample/png2prg 2/07_lilia.prg b/loader/samples/minexample/png2prg 2/07_lilia.prg new file mode 100644 index 0000000..81267d6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/07_lilia.prg differ diff --git a/loader/samples/minexample/png2prg 2/08_lilia_hero.prg b/loader/samples/minexample/png2prg 2/08_lilia_hero.prg new file mode 100644 index 0000000..6d63f9b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/08_lilia_hero.prg differ diff --git a/loader/samples/minexample/png2prg 2/11_lilia_final.csv b/loader/samples/minexample/png2prg 2/11_lilia_final.csv new file mode 100644 index 0000000..fb9202a --- /dev/null +++ b/loader/samples/minexample/png2prg 2/11_lilia_final.csv @@ -0,0 +1,66 @@ +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +50,ys2/11_lilia_final/koala/png/01.png + +20,ys2/11_lilia_final/koala/png/04.png +3,ys2/11_lilia_final/koala/png/05.png +3,ys2/11_lilia_final/koala/png/06.png +3,ys2/11_lilia_final/koala/png/07.png +3,ys2/11_lilia_final/koala/png/06.png +3,ys2/11_lilia_final/koala/png/05.png +3,ys2/11_lilia_final/koala/png/04.png + +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png + +3,ys2/11_lilia_final/koala/png/08.png +3,ys2/11_lilia_final/koala/png/09.png +3,ys2/11_lilia_final/koala/png/10.png +3,ys2/11_lilia_final/koala/png/11.png +3,ys2/11_lilia_final/koala/png/10.png +3,ys2/11_lilia_final/koala/png/09.png +3,ys2/11_lilia_final/koala/png/08.png + +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/01.png +7,ys2/11_lilia_final/koala/png/02.png +7,ys2/11_lilia_final/koala/png/03.png +7,ys2/11_lilia_final/koala/png/02.png +50,ys2/11_lilia_final/koala/png/01.png + +3,ys2/11_lilia_final/koala/png/12.png +3,ys2/11_lilia_final/koala/png/13.png +3,ys2/11_lilia_final/koala/png/14.png +3,ys2/11_lilia_final/koala/png/15.png +200,ys2/11_lilia_final/koala/png/16.png \ No newline at end of file diff --git a/loader/samples/minexample/png2prg 2/11_lilia_final.prg b/loader/samples/minexample/png2prg 2/11_lilia_final.prg new file mode 100644 index 0000000..a661a82 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/11_lilia_final.prg differ diff --git a/loader/samples/minexample/png2prg 2/examples.d64 b/loader/samples/minexample/png2prg 2/examples.d64 new file mode 100644 index 0000000..692f4d3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/examples.d64 differ diff --git a/loader/samples/minexample/png2prg 2/lilia.csv b/loader/samples/minexample/png2prg 2/lilia.csv new file mode 100644 index 0000000..268e37d --- /dev/null +++ b/loader/samples/minexample/png2prg 2/lilia.csv @@ -0,0 +1,9 @@ +50,ys2/07_lilia/lilia1.png +4,ys2/07_lilia/lilia2.png +4,ys2/07_lilia/lilia3.png +4,ys2/07_lilia/lilia4.png +4,ys2/07_lilia/lilia5.png +4,ys2/07_lilia/lilia6.png +4,ys2/07_lilia/lilia7.png +4,ys2/07_lilia/lilia8.png +150,ys2/07_lilia/lilia9.png diff --git a/loader/samples/minexample/png2prg 2/lilia.prg b/loader/samples/minexample/png2prg 2/lilia.prg new file mode 100644 index 0000000..bc84828 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/lilia.prg differ diff --git a/loader/samples/minexample/png2prg 2/png2prg_darwin_arm64 b/loader/samples/minexample/png2prg 2/png2prg_darwin_arm64 new file mode 100755 index 0000000..f36afcf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/png2prg_darwin_arm64 differ diff --git a/loader/samples/minexample/png2prg 2/png2prg_win_x86.exe b/loader/samples/minexample/png2prg 2/png2prg_win_x86.exe new file mode 100644 index 0000000..0b547db Binary files /dev/null and b/loader/samples/minexample/png2prg 2/png2prg_win_x86.exe differ diff --git a/loader/samples/minexample/png2prg 2/readme.md b/loader/samples/minexample/png2prg 2/readme.md new file mode 100644 index 0000000..ad40513 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/readme.md @@ -0,0 +1,687 @@ +# PNG2PRG 1.12 by burg + +Png2prg converts a 320x200 image (png/gif/jpeg) to a c64 hires or +multicolor bitmap, charset, petscii, ecm or sprites prg. It will find the best +matching palette and background/bitpair-colors automatically, no need to modify +your source images or configure a palette. + +Vice screenshots with default borders (384x272) are automatically cropped. +Vice's main screen offset is at x=32, y=35. +Quite a few people (and possibly tools too) use the incorrect 32,36 offset. +Use the -alt-offset or -ao flag to use 32,36 as offset. +Images in sprite dimensions will be converted to sprites. + +The resulting .prg includes the 2-byte start address and optional displayer. +The displayers can optionally play a .sid tune. + +This tool can be used in all buildchains on all common platforms. + +## What Is New + +Png2prg 1.12 introduces animation.csv support for custom delays per frame. +See 'Animation csv' below for details. +The -no-loop flag causes animations to only display once. + +This release contains an important bugfix related to mixedcharsets, +where in some cases, png2prg would require more unique chars than necessary. +There were more issues with png2prg 1.10, which were hotfixed in 1.10.1. +It is best to delete any 1.10 version and upgrade to 1.12. + +ECM conversion has been improved, now png2prg also searches for potential +char reduction by searching for invertable characters. + +Trident added [devcontainer files](https://github.com/staD020/png2prg/commit/6cb6c48a2804fa5210cf704e0af4cff3313398fe) for setting up a Docker +development environment to compile png2prg directly from within VSCode. + +See 'Changes for version 1.12' below for more features and details. + +## What it is *not* + +Png2prg is not a tool to wire fullcolor images. It needs input images to +already be compliant with c64 color and size restrictions. +In verbose mode (-v) it outputs locations of color clashes, if any. + +If you do need to wire fullcolor images, check out Youth's [Retropixels](https://www.micheldebree.nl/retropixels/). + +## Supported Graphics Modes + + koala: multicolor bitmap (max 4 colors per char) + hires: singlecolor bitmap (max 2 colors per char) + mixedcharset: multicolor charset (max 4 colors per char (fixed bgcol, d022, d023)) + mccharset: multicolor charset (max 4 colors) + sccharset: singlecolor charset (max 2 colors per char (fixed bgcol)) + petscii: singlecolor rom charset (max 2 colors per char (fixed bgcol)) + ecm: singlecolor charset (max 2 colors per char (4 fixed bgcolors), max 64 chars) + mcsprites: multicolor sprites (max 4 colors) + scsprites: singlecolor sprites (max 2 colors) + mcibitmap: 320x200 multicolor interlace bitmap (max 4 colors per char/frame) + +Png2prg is mostly able to autodetect the correct graphics mode, but you can +also force a specific graphics mode with the -mode flag: + + ./png2prg -m koala image.png + +## Koala or Hires Bitmap + + Bitmap: $2000 - $3f3f + Screen: $3f40 - $4327 + D020: $4328 (singlecolor only) + D800: $4328 - $470f (multicolor only) + D021: $4710 (multicolor only, low-nibble) + D020: $4710 (multicolor only, high-nibble) + +## Multicolor Interlace Bitmap + +You can supply one 320x200 multicolor image with max 4 colors per 8x8 pixel +char per frame of which at least 2 are shared (the D021 and D800 colors). + +Or supply both frames in regular koala specs (-interlace flag required). +When making screenshots in vice, please disable the d016 pixel shift manually. + + ./png2prg -i testdata/madonna/frame_0.png testdata/madonna/frame_1.png + +### Drazlace (shared screenram and colorram for both frames) + + ./png2prg testdata/madonna/cjam_pure_madonna.png + + D800: $5800 - $5be7 + Screen: $5c00 - $5fe7 + Bitmap1: $6000 - $7f3f + D021: $7f40 (low-nibble) + D020: $7f40 (high-nibble) + D016Offset: $7f42 + Bitmap2: $8000 - $9f3f + +### Multicolor Interlace (shared colorram, true paint .mci format) + + ./png2prg -i -d016 1 testdata/mcinterlace/parriot?.png + + Screen1: $9c00 - $9fe7 + D021: $9fe8 (low-nibble) + D020: $9fe8 (high-nibble) + D016Offset: $9fe9 + Bitmap1: $a000 - $bf3f + Bitmap2: $c000 - $df3f + Screen2: $e000 - $e3e7 + D800: $e400 - $e7e7 + +## Singlecolor, PETSCII or ECM Charset (individual d800 colors) + +By default charsets are packed, they only contain unique characters. +If you do not want charpacking, eg for a 1x1 charset, please use -no-pack. + +With ECM -bitpair-colors can be used to force d021-d024 colors. + +NB: individual d800 colors are not supported with -no-pack. + + ./png2prg -m sccharset testdata/hirescharset/ohno_logo.png + ./png2prg -m petscii testdata/petscii/hein_hibiscus.png + ./png2prg -m ecm testdata/ecm/xpardey.png + ./png2prg -m ecm testdata/ecm/shampoo.png + ./png2prg -m ecm -bpc 2,7,14,0 testdata/ecm/orion.png + + Charset: $2000-$27ff (omitted for petscii) + Screen: $2800-$2be7 + D800: $2c00-$2fe7 + D020: $2fe8 + D021: $2fe9 + D022: $2fea (ecm only) + D023: $2feb (ecm only) + D024: $2fec (ecm only) + +## Mixed Multi/Singlecolor Charset (individual d800 colors) + +Png2prg tries to figure out the right -bitpair-colors and auto-corrects +where it can, but there still are edge-cases like the ones below. +If an impossible color is found, an error will be displayed. +Swap some -bpc colors around and retry. +There can also be cases where manual -bpc colors can influence char-count or +packed size. + +You may want to add the -brute-force flag so most color options will be tried. +The best packed result wins, not necessarily the version with the least amount +of chars. + + ./png2prg -m mixedcharset testdata/mixedcharset/hein_neo.png + ./png2prg -m mixedcharset testdata/mixedcharset/huntress.gif + ./png2prg -m mixedcharset -bpc 3 testdata/mixedcharset/shine.png + ./png2prg -m mixedcharset -bpc 0 testdata/mixedcharset/charsetcompo.png + + Charset: $2000-$27ff + Screen: $2800-$2be7 + D800: $2c00-$2fe7 + D020: $2fe8 + D021: $2fe9 + D022: $2fea + D023: $2feb + +## Single or Multicolor Sprites + +If the source image size is a multiple of a 24x21 pixel sprite, +the image is considered to contain sprites. + +The image will be converted from left to right, top to bottom. + + ./png2prg image.png + ./png2prg -m scsprites image.png + ./png2prg -m mcsprites image.png + + Sprite 1: $2000-$203f + Sprite 2: $2040-$207f + ... + +## Bitpair Colors + +By default, png2prg guesses bitpair colors by itself. In most cases you +don't need to configure anything. It will provide a mostly normalized image +which should yield good pack results, but your miles may vary. + +To give you more control, you can force/prefer a specific bitpair +color-order. Use c64 colors, so 0 for black, 1 for white, 2 for red, etc. + +The following example will force background color 0 for bitpair 00 and +prefer colors 6,14,3 for bitpairs 01,10,11: + + ./png2prg -bitpair-colors 0,6,14,3 image.png + +It's also possible to explicitly skip certain bitpair preferences with -1: + + ./png2prg -bitpair-colors 0,-1,-1,3 image.png + +## Animations + +When multiple files are added, they are treated as animation frames. +You can also supply an animated .gif. + +## Sprite Animation + +Each frame will be concatenated in the output .prg. + +## Bitmap Animation (only koala and hires) + +Note that png2prg uses a rather simple generic diff approach, where small +changes frame by frame work well. Trying to change large areas at once +is not advised. +Use the -no-fade flag if you run out of memory. + +The first image will be exported with all framedata appended. +Koala animation frames start at $4711, hires at $4329. + +The frame files are following this format. +Each frame consists of 1 or more chunks. A chunk looks like this: + + .byte $03 // number of chars in this chunk + // $00 marks end of frame + // $ff marks end of all frames + .word bitmap // bitmap address of this chunk (the high byte is <$20) + .word screen // screenram address (the high byte is <$04) + + For each char in this chunk: + + .byte 0,31,15,7,8,34,0,128 // pixels + .byte $64 // screenram colors + .byte $01 // colorram color (koala only) + ... // next char(s) + + ... // next chunks + .byte 0 // end of frame + .byte 6 // wait for 6 frames + ... // next frame(s) + .byte $ff // end of all frames + +## PETSCII and Charset Animation + +Only petscii and sccharset modes support different background and +bordercolors per frame. +All chars used in all frames must fit into a single 256 char charset. + +Each frame consists of 1 or more chunks. A chunk looks like this: + + .byte $xy // $y = bgcol, $x = bordercol (only for petscii/sccharset) + .byte $03 // number of chars in this chunk + // $00 marks end of frame + // $ff marks end of all frames + .word screen // screenram address (the high byte is <$04) + + For each char in this chunk: + + .byte $03 // character + .byte $01 // colorram color + ... // next char(s) + + ... // next chunks + .byte 0 // end of frame + .byte 6 // wait for 6 frames + ... // next frame(s) + .byte $ff // end of all frames + +## Animation csv + +Since version 1.12 animation.csv support has been added to give more +freedom to users wanting to create animation displayers. +It is now possible to use a custom frame-delay per frame. + +The csv should contain rows of frame delay value and image, where the +delay can be any value from 0 till 255. The highest delay is a little +over 5 seconds on PAL systems. +If you want longer delays, just copy a row. + + 10,frame0.png + 50,frame1.png + 10,frame2.png + +Examples can be found here: [Évoluer by The Sarge](https://github.com/staD020/png2prg/blob/master/testdata/evoluer/evoluer.csv) and [Rose by Sander](https://github.com/staD020/png2prg/blob/master/testdata/petscii/anim/rose.csv) + + png2prg -d -o evoluer.prg -sid testdata/evoluer/Evoluer.sid testdata/evoluer/evoluer.csv + png2prg -d -o rose.prg testdata/petscii/anim/rose.csv + +## Displayer + +The -d or -display flag will link displayer code infront of the picture. +By default it will also crunch the resulting file with Antonio Savona's +[TSCrunch](https://github.com/tonysavon/TSCrunch/) with a couple of changes in my own [fork](https://github.com/staD020/TSCrunch/). + +All displayers except for sprites support adding a .sid. +Multispeed sids are supported as long as the .sid initializes the CIA timers +correctly. + +You can use sids located from $0e00-$1fff or $e000+ in the displayers. +More areas may be free depending on graphics type. +A memory usage map is shown on error and in -vv (very verbose) mode. + +If needed, you can relocate most sids using lft's [sidreloc](http://www.linusakesson.net/software/sidreloc/index.php). + +Zeropages $08-$0f are used in the animation displayers, while none are used +in hires/koala displayers, increasing sid compatibility. + +## Brute Force Mode and Pack Optimization + +By default png2prg 1.8 does a pretty good job at optimizing the resulting prg +for crunchers and packers. It is not enough to beat [SPOT 1.3](https://csdb.dk/release/?id=242492). + +The optimization techniques used by png2prg are also responsible for cleaning +up the bitmap, making it ideal for animations and color effects. + +### -brute-force (-bf) + +Iterates are over many -bitpair-colors permutations automatically, packs +with the built in TSCrunch and selects the shortest. + + ./png2prg -bf image.png + +The -brute-force mode can be used in combination with additional flags. + +### -no-bitpair-counters (-nbc) + +Disable counting of bitpairs per color to guess a bitpair for a color. + + ./png2prg -bf -nbc image.png + +### -no-prev-char-colors (-npcc) + +Disable lookback to previous char's charcolors to guess a bitpair for a color. + + ./png2prg -bf -npcc image.png + +Since TSCrunch is optimized for speed, packing with Dali can give varying +results. This is also the reason for not including these options in the +brute force permutations automatically. + +## Benchmark + +The [koala otpimizing thread](https://csdb.dk/forums/?roomid=13&topicid=38311&showallposts=1) on csdb has gained some interest in the scene. +Since Sparta released [SPOT](https://csdb.dk/release/?id=242492) it has been the best optimizer available. + +Png2prg 1.8 has improved optimization techniques but requires -brute-force +mode to beat SPOT 1.3. Manual flags can optimize even better in some cases. + +All koalas are packed with [Dali 0.3.2](https://csdb.dk/release/?id=223584). + + +---------+--------+----------+------------+--------+ + | spot1.3 | p2p1.8 | p2p1.8bf | p2p1.8best | p2p1.6 | + +---------+--------+----------+------------+--------+ + | 7332 | 7372 | 7332 | 7324 | 7546 | Untitled/Floris + | 5136 | 5190 | 5149 | bf | 5464 | Song of the Sunset/Mermaid + | 5968 | 5998 | 5963 | bf | 6155 | Short Circuit/Karen Davies + | 3618 | 3647 | 3616 | 3589 | 3830 | Portrait L+D/Sander + | 5094 | 5080 | 5083 | 5078 | 5320 | Weee/Mermaid + | 7497 | 7471 | 7458 | bf | 7612 | Deadlock/Robin Levy + | 8068 | 8097 | 8046 | 8038 | 8227 | Room with a view/Veto + | 7445 | 7490 | 7432 | bf | 7582 | Vangelis/Talent + | 6759 | 6739 | 6737 | bf | 6963 | Temple of Tears/Hend + | 7859 | 7848 | 7839 | 7821 | 7998 | Thanos/JonEgg + | 4859 | 4849 | 4782 | bf | 4983 | Solar-Sonar/Leon + | 5640 | 5671 | 5613 | bf | 5869 | Cisco Heat/Alan Grier + | 6243 | 6286 | 6228 | bf | 6430 | Daylight/Sulevi + | 2850 | 2884 | 2848 | bf | 3092 | Yie Ar Kung Fu/Steve Wahid + | 6727 | 6721 | 6730 | 6711 | 6901 | Lee/The Sarge + | 7837 | 7828 | 7798 | bf | 7960 | Parrot/Mirage + | 4559 | 4536 | 4494 | bf | 4821 | Dragon's Lair + | 4275 | 4324 | 4292 | 4284 | 4519 | Scorpion/SIR'88 + | 5562 | 5558 | 5506 | bf | 5668 | Hatching/Joe + +---------+--------+----------+------------+--------+ + | 113328 | 113589 | 112946 | 112853 | 116940 | Total + +---------+--------+----------+------------+--------+ + + - p2p1.8: default png2prg result w/o options + - p2p1.8bf: -brute-force mode + - p2p1.8best: hand-picked -bitpair-colors, or bruteforced with -npcc and/or -nbc flags + - p2p1.6: default png2prg 1.6 result w/o options + +## Examples + +This release contains examples with all assets included for you to test with. +Also included are the assets of [Évoluer](https://csdb.dk/release/?id=220170) by The Sarge and Flotsam. +A larger set of testdata can be found in the [github repo](https://github.com/staD020/png2prg/tree/master/testdata). + +## Install from source + +Png2prg was built on Linux, building on Mac should work out of the box. +For Windows, try out Windows Subsystem Linux (WSL), works pretty well. +However, natively building on Windows should be easy enough, look at +Compiling without Make below. + +The compiled displayer prgs are included in the repo to ease building +and importing png2prg as a library. Java is only required to build +the displayers with KickAssembler (included in the repo). + +But first [install Go 1.20 or higher](https://go.dev/dl/). + +### Simple install + + go install -v github.com/staD020/png2prg@master + +### Compiling with Make (recommended) + + git clone https://github.com/staD020/png2prg.git + cd png2prg + make -j + +Build for all common targets: + + make all -j + +### Compiling without Make + + go build ./cmd/png2prg + +## Install and use as library + +In your Go project's path, go get the library: + + go get github.com/staD020/png2prg + +In essence png2prg implements the [io.WriterTo](https://pkg.go.dev/io#WriterTo) interface. +Typical usage could look like below. A more complex example can be found +in the [source](https://github.com/staD020/png2prg/blob/master/cmd/png2prg/main.go) of the cli tool. + +```go +import ( + "fmt" + "io" + "github.com/staD020/png2prg" +) + +func convertPNG(w io.Writer, png io.Reader) (int64, error) { + p, err := png2prg.New(png2prg.Options{}, png) + if err != nil { + return 0, fmt.Errorf("png2prg.New failed: %w", err) + } + return p.WriteTo(w) +} +``` + +## Changes for version 1.12 + + - Bugfix: Fix regression with handling mixedcharsets that was increasing + char usage count in some cases (thanks Shine). + - Feature: Press CBM key in petscii (animation) displayers to switch + charset case. + - Feature: Add -brute-force support to all charset modes. + - Feature: Improve ECM handling by searching for invertable characters to + reduce char usage. + - Feature: Add animation.csv support to allow for custom delays per frame. + - Feature: Add -no-loop support for animations (thanks jab). + - Feature: Add -no-fade support to other displayers (thanks Shine). + - Feature: Disable repeating color optimization for koala & hires anims. + This reduces animation size & runtime processing at the cost of initial + image optimization. + - Feature: Allow sids to use all memory below $0400 (thanks kbs). + - Feature: VSCode Docker build support for a devcontainer was added by + Trident (thanks!). + - Experimental: Add secondary+tertiary preferred bitpair colors with -bpc2 + and -bpc3 (thanks Fungus). + +## Changes for version 1.10.1 + + - Play NTSC .sid tunes at the right speed (thanks Acrouzet). + - Bugfix: palette detection must detect all hires colors, also on odd pixels. + - Bugfix: dont look at border area to determine hires pixels. + - Bugfix: fix -frame-delay for charset anim displayers. + +## Changes for version 1.10 + + - Add gfxmode to .sym files and display in terminal output (thanks Spider-J). + - Add petscii animation support. + - Add background and bordercolor to each petscii or sccharset animation frame. + - Add -no-anim flag disable sc/mccharset animations and store frames as separate + screens. + - Add -no-fade flag for koala, hires, petscii and sccharset animation + displayers, this frees up a lot of RAM for animation data and sid. + - Code refactor, standardizing color and bitpair code, separated palettes in + palettes.yaml and more. + - Bugfix: repair -force-border-color. + - Bugfix: handle blank ECM images as well as ECM images using few bg colors + (thanks Brush). + - Typofix: fix simple install docs (thanks IcePic). + - Added another weird palette (thanks Fungus). + +## Changes for version 1.8 + + - Improve crunchiness by re-using the previous char's bitpair-colors. + - Add -no-prev-char-colors flag to disable re-use of the previous char's + bitpair-colors, in some cases this optimization causes worse pack results. + - Add -brute-force mode to find bitpair color combinations with better + crunchiness. Burns some CPU for a couple seconds. + - Add -no-bitpair-counters flag to disable using bitpair counters per color + for color guessing. + - Added multi-frame support for mccharset, where all frames use the same + charset. + - Add support for any centered fullscreen image resolution bigger than + 320x200 and other than 384x272. + - Add support for Marq's PETSCII tool .png resolution 352x232 (thanks jab). + - Bugfix: docs fixes related to installation from source (thanks jab). + - Bugfix: hide findECMColors log behind -verbose mode (thanks jab). + - Docs fix: add a bit more info for sprites (thanks fungus). + +## Changes for version 1.6 + + - Added -mode mixedcharset for mixed multicolor/singlecolor and + individual d800 colors per char. + - Modified -mode sccharset to use individual d800 colors per char. + - Added -mode petscii. + - Added -mode ecm. + - Added -no-pack-empty to skip packing empty chars to filled chars to re-use + for different colors. Only for mixed and ecm charsets. + - Added -force-pack-empty for singlecolor and multicolor charset, may save + a char, but usually pack-ratio is worse due to increased d800 color usage. + - Improved auto-detection of graphics modes, including various charset modes. + - Added sid support to charset displayers. + - Added fullscreen fade in/out to charset displayers. + - Bug Fix: -force-border-color for singlecolor charset (thanks Raistlin). + - Bug Fix: do not write empty .prg file on error. + - Standardized d02x colors in output.prg for charset modes. + +## Changes for version 1.4 + + - Support for even more far-out palette ranges (thanks Perplex). + - Now throws an error if the palette can't be detected properly, this should + never happen. Please let me know if you run into this error. + - Separated library and cli tool. + - Library supports the standard [io.Reader](https://pkg.go.dev/io#Reader) and [io.Writer](https://pkg.go.dev/io#Writer) interfaces. + - Patched [TSCrunch](https://github.com/staD020/TSCrunch/) further to increase crunch speed and use less memory. + - Added -parallel and -worker flags to treat each input file as standalone + and convert all files in parallel. Gifs with multiple frames are still + treated as animations. + - Stop relying on .gif filename extension, detect it. + - Add -alt-offset flag to force screenshot offset 32, 36), used by a few + graphicians. Though, please switch to the correct 32, 35. + - Add -symbols flag to write symbols to a .sym file. + - Interlace support for mcibitmap (drazlace and truepaint). + - Bugfix: allow blank images input (thanks Spider-J). + - Allow colors not present in the image as -bitpair-colors (thanks Map). + +## Changes for version 1.2 + + - Added displayer for koala animations. + - Added displayer for hires animations. + - Added -frame-delay flag for animation displayers. + - Added -wait-seconds flag for animation displayers. + - Fixed bug in koala/hires displayers not allowing sids to overlap $c000-$c7ff. + - Expanding wildcards: using pic??.png or pic*.png now also works on Windows. + - Set bank via $dd00 in displayers. + +## Changes for version 1.0 + + - Added fullscreen fade in/out to koala and hires displayers. + - Added optional .sid support for koala and hires displayers. + - Added optional crunching for all displayers using TSCrunch. + +## Credits + +Png2prg was created by Burglar, using the following third-party libraries: + +[TSCrunch 1.3](https://github.com/tonysavon/TSCrunch/) by Antonio Savona for optional crunching when exporting +an image with a displayer. + +[Colfade Doc](https://csdb.dk/release/?id=132276) by Veto for the color fade tables used in the displayers. + +[Kick Assembler](http://www.theweb.dk/KickAssembler/) by Slammer to compile the displayers. + +[Go](https://go.dev/) by The Go Authors is the programming language used to create png2prg. + +### Thanks to + +Apollyon, Spider-J, Brush, The Sarge, Fungus, Jab, Shine, Raistlin, Perplex, +Map, Youth, IcePic, Sander, Guinea Pig, Krill, Christopher Jam, Sparta, +Acrouzet, Trident, Worrior1 and Antonio Savona. + +## Options + +``` + -alt-offset + use alternate screenshot offset with x,y = 32,36 + -ao + alt-offset + -bf + brute-force + -bitpair-colors string + prefer these colors in 2bit space, eg 0,6,14,3 + -bpc string + bitpair-colors + -bpc2 string + secondary bitpair colors eg 0,2,10,7 + -bpc3 string + tertiary bitpair colors eg 0,11,12,15 + -brute-force + brute force bitpair-colors + -cpuprofile file + write cpu profile to file + -d display + -d016 int + d016offset (default 1) + -d016offset int + number of pixels to shift with d016 when using interlace (default 1) + -display + include displayer + -force-border-color int + force border color (default -1) + -force-pack-empty + optimize packing empty chars (only for sccharset) + -fpe + force-pack-empty + -frame-delay int + frames to wait before displaying next animation frame (default 6) + -h help + -help + help + -i interlace + -interlace + when you supply 2 frames, specify -interlace to treat the images as such + -m string + mode + -memprofile file + write memory profile to file (only in -parallel mode) + -mode string + force graphics mode to koala, hires, mixedcharset, sccharset, mccharset (4col), scsprites or mcsprites + -na + no-anim + -nbc + no-bitpair-counters + -nc + no-crunch + -nf + no-fade + -ng + no-guess + -nl + no-loop + -no-anim + disable charset animations and store frames as separate screens + -no-bitpair-counters + do not use c64color bitpar counters optimization + -no-crunch + do not TSCrunch displayer + -no-fade + do not use fade in/out and free up a lot of memory + -no-guess + do not guess preferred bitpair-colors + -no-loop + play animations only once + -no-pack + do not pack chars (only for sc/mc charset) + -no-pack-empty + do not optimize packing empty chars (only for mc/mixed/ecm charset) + -no-prev-char-colors + do not look at the previous char's bitpair-colors, in some cases this optimization causes worse pack results + -np + no-pack + -npcc + no-prev-char-colors + -npe + no-pack-empty + -o string + out + -out string + specify outfile.prg, by default it changes extension to .prg + -p parallel + -parallel + run number of workers in parallel for fast conversion, treat each image as a standalone, not to be used for animations, unless an anim.csv is used + -q quiet + -quiet + quiet, only display errors + -sid string + include .sid in displayer (see -help for free memory locations) + -sym + symbols + -symbols + export symbols to .sym + -targetdir string + specify targetdir + -td string + targetdir + -trd + has side effect of enforcing screenram bitpair colors in level area + -v verbose + -verbose + verbose output + -vv + very verbose, show memory usage map in most cases and implies -verbose + -w int + workers (default 12) + -wait-seconds int + seconds to wait before animation starts + -workers int + number of concurrent workers in -parallel or -brute-force mode (default 12) +``` + diff --git a/loader/samples/minexample/png2prg 2/title_ifli.png b/loader/samples/minexample/png2prg 2/title_ifli.png new file mode 100644 index 0000000..7b305a5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/title_ifli.png differ diff --git a/loader/samples/minexample/png2prg 2/title_ifli_i1.png b/loader/samples/minexample/png2prg 2/title_ifli_i1.png new file mode 100644 index 0000000..5b962ec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/title_ifli_i1.png differ diff --git a/loader/samples/minexample/png2prg 2/title_ifli_i1_2x.png b/loader/samples/minexample/png2prg 2/title_ifli_i1_2x.png new file mode 100644 index 0000000..a16db8e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/title_ifli_i1_2x.png differ diff --git a/loader/samples/minexample/png2prg 2/title_ifli_i2.png b/loader/samples/minexample/png2prg 2/title_ifli_i2.png new file mode 100644 index 0000000..2227922 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/title_ifli_i2.png differ diff --git a/loader/samples/minexample/png2prg 2/title_ifli_i2_2x.png b/loader/samples/minexample/png2prg 2/title_ifli_i2_2x.png new file mode 100644 index 0000000..258ba7a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/title_ifli_i2_2x.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2.csv b/loader/samples/minexample/png2prg 2/ys2.csv new file mode 100644 index 0000000..2b88a26 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2.csv @@ -0,0 +1,29 @@ + +5,ys2/01_badguyz/koala/png/01.png +5,ys2/01_badguyz/koala/png/02.png +5,ys2/01_badguyz/koala/png/03.png +5,ys2/01_badguyz/koala/png/04.png +5,ys2/01_badguyz/koala/png/05.png +50,ys2/01_badguyz/koala/png/06.png +15,ys2/01_badguyz/koala/png/07.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/11.png +100,ys2/01_badguyz/koala/png/12.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/07.png +100,ys2/01_badguyz/koala/png/06.png +5,ys2/01_badguyz/koala/png/13.png +5,ys2/01_badguyz/koala/png/14.png +5,ys2/01_badguyz/koala/png/15.png +5,ys2/01_badguyz/koala/png/16.png +5,ys2/01_badguyz/koala/png/17.png +5,ys2/01_badguyz/koala/png/18.png +5,ys2/01_badguyz/koala/png/19.png +100,ys2/01_badguyz/koala/png/00.png + + + diff --git a/loader/samples/minexample/png2prg 2/ys2.prg b/loader/samples/minexample/png2prg 2/ys2.prg new file mode 100644 index 0000000..8d72c17 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2.prg differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/00.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/00.bmp new file mode 100644 index 0000000..82de4ee Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/00.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/01.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/01.bmp new file mode 100644 index 0000000..65e7f04 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/02.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/02.bmp new file mode 100644 index 0000000..1d48923 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/03.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/03.bmp new file mode 100644 index 0000000..41e88eb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/04.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/04.bmp new file mode 100644 index 0000000..98c4b2d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/05.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/05.bmp new file mode 100644 index 0000000..35fbea2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/06.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/06.bmp new file mode 100644 index 0000000..317c234 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/07.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/07.bmp new file mode 100644 index 0000000..b6ba708 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/08.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/08.bmp new file mode 100644 index 0000000..aee0f7d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/09.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/09.bmp new file mode 100644 index 0000000..e9fa5de Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/10.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/10.bmp new file mode 100644 index 0000000..902fcb8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/11.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/11.bmp new file mode 100644 index 0000000..0846091 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/12.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/12.bmp new file mode 100644 index 0000000..b15dc55 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/13.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/13.bmp new file mode 100644 index 0000000..49ca24e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/14.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/14.bmp new file mode 100644 index 0000000..a8a6e42 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/15.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/15.bmp new file mode 100644 index 0000000..91edd40 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/16.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/16.bmp new file mode 100644 index 0000000..c33752c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/17.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/17.bmp new file mode 100644 index 0000000..9e195b6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/18.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/18.bmp new file mode 100644 index 0000000..1d3bf2c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/19.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/19.bmp new file mode 100644 index 0000000..39509d6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/00.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/00.bmp new file mode 100644 index 0000000..958551c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/00.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/01.bmp new file mode 100644 index 0000000..df502bb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/02.bmp new file mode 100644 index 0000000..9def36a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/03.bmp new file mode 100644 index 0000000..9c2509d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/04.bmp new file mode 100644 index 0000000..2f7961e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/05.bmp new file mode 100644 index 0000000..59857ec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/06.bmp new file mode 100644 index 0000000..ad1acd2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/07.bmp new file mode 100644 index 0000000..12b8b14 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/08.bmp new file mode 100644 index 0000000..6722858 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/09.bmp new file mode 100644 index 0000000..bcf8dc2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/10.bmp new file mode 100644 index 0000000..c05de60 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/11.bmp new file mode 100644 index 0000000..52d4d08 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/12.bmp new file mode 100644 index 0000000..e18b0e4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/13.bmp new file mode 100644 index 0000000..26c797c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/14.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/14.bmp new file mode 100644 index 0000000..0d0a1cd Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/15.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/15.bmp new file mode 100644 index 0000000..d8fd26c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/16.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/16.bmp new file mode 100644 index 0000000..cf7bab3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/17.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/17.bmp new file mode 100644 index 0000000..3b4941e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/18.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/18.bmp new file mode 100644 index 0000000..523831a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/19.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/19.bmp new file mode 100644 index 0000000..84daa34 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/00.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/00.bmp new file mode 100644 index 0000000..958551c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/00.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/01.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/01.bmp new file mode 100644 index 0000000..df502bb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/02.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/02.bmp new file mode 100644 index 0000000..9def36a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/03.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/03.bmp new file mode 100644 index 0000000..9c2509d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/04.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/04.bmp new file mode 100644 index 0000000..2f7961e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/05.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/05.bmp new file mode 100644 index 0000000..59857ec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/06.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/06.bmp new file mode 100644 index 0000000..ad1acd2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/07.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/07.bmp new file mode 100644 index 0000000..12b8b14 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/08.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/08.bmp new file mode 100644 index 0000000..6722858 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/09.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/09.bmp new file mode 100644 index 0000000..bcf8dc2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/10.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/10.bmp new file mode 100644 index 0000000..c05de60 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/11.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/11.bmp new file mode 100644 index 0000000..52d4d08 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/12.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/12.bmp new file mode 100644 index 0000000..e18b0e4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/13.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/13.bmp new file mode 100644 index 0000000..26c797c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/14.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/14.bmp new file mode 100644 index 0000000..0d0a1cd Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/15.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/15.bmp new file mode 100644 index 0000000..d8fd26c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/16.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/16.bmp new file mode 100644 index 0000000..cf7bab3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/17.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/17.bmp new file mode 100644 index 0000000..3b4941e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/18.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/18.bmp new file mode 100644 index 0000000..523831a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/19.bmp b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/19.bmp new file mode 100644 index 0000000..84daa34 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/bak/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/00.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/00.png new file mode 100644 index 0000000..4b2372d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/00.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01.png new file mode 100644 index 0000000..4926a96 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01_badguyz.zip b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01_badguyz.zip new file mode 100644 index 0000000..ab81550 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/01_badguyz.zip differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/02.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/02.png new file mode 100644 index 0000000..24e22a6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/03.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/03.png new file mode 100644 index 0000000..0e8066d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/04.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/04.png new file mode 100644 index 0000000..6a483db Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/05.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/05.png new file mode 100644 index 0000000..663c8b1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/06.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/06.png new file mode 100644 index 0000000..9a1324c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/07.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/07.png new file mode 100644 index 0000000..1f42495 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/08.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/08.png new file mode 100644 index 0000000..0bb16f1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/09.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/09.png new file mode 100644 index 0000000..23fb92d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/10.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/10.png new file mode 100644 index 0000000..e957043 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/11.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/11.png new file mode 100644 index 0000000..deb7f0a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/12.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/12.png new file mode 100644 index 0000000..fbe1be5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/13.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/13.png new file mode 100644 index 0000000..92d5dce Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/14.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/14.png new file mode 100644 index 0000000..3d2dcd4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/15.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/15.png new file mode 100644 index 0000000..7e4f869 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/16.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/16.png new file mode 100644 index 0000000..7c09dac Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/16.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/17.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/17.png new file mode 100644 index 0000000..3315ccc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/17.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/18.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/18.png new file mode 100644 index 0000000..94bb90f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/18.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/19.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/19.png new file mode 100644 index 0000000..de648e4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/cropped/19.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/00.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/00.png new file mode 100644 index 0000000..1de54c8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/00.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/01.png new file mode 100644 index 0000000..9e48d61 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/02.png new file mode 100644 index 0000000..e59d44a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/03.png new file mode 100644 index 0000000..b6dad32 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/04.png new file mode 100644 index 0000000..6053816 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/05.png new file mode 100644 index 0000000..1d22c6b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/06.png new file mode 100644 index 0000000..6c27038 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/07.png new file mode 100644 index 0000000..e449883 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/08.png new file mode 100644 index 0000000..8d03c5d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/09.png new file mode 100644 index 0000000..ccf5c76 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/10.png new file mode 100644 index 0000000..198376d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/11.png new file mode 100644 index 0000000..a563e5f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/12.png new file mode 100644 index 0000000..3e375e9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/13.png new file mode 100644 index 0000000..87a6565 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/14.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/14.png new file mode 100644 index 0000000..70c2277 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/15.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/15.png new file mode 100644 index 0000000..d75d087 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/16.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/16.png new file mode 100644 index 0000000..5c65324 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/16.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/17.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/17.png new file mode 100644 index 0000000..90786ea Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/17.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/18.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/18.png new file mode 100644 index 0000000..35af5ee Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/18.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/19.png b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/19.png new file mode 100644 index 0000000..afc11e2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/01_badguyz/koala/png/19.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title.bmp b/loader/samples/minexample/png2prg 2/ys2/02_title/title.bmp new file mode 100644 index 0000000..432e617 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title.nuf b/loader/samples/minexample/png2prg 2/ys2/02_title/title.nuf new file mode 100644 index 0000000..05ecebf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title.nuf differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title.png new file mode 100644 index 0000000..d91f4c3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title.prg b/loader/samples/minexample/png2prg 2/ys2/02_title/title.prg new file mode 100644 index 0000000..a939ac6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title.prg differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-prepared.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-prepared.png new file mode 100644 index 0000000..b3f72bf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-prepared.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-result.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-result.png new file mode 100644 index 0000000..958b453 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-result.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-speedcode.txt b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-speedcode.txt new file mode 100644 index 0000000..3caae27 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared-speedcode.txt @@ -0,0 +1,236 @@ +0* | 0 | 0fff | | 2 | free cc: 36/59 | d018:68[10-55] d011:3a[58-58] + | | | 907198 | | a=38 x=68 y=3a | V_18=X; A=58; X=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +1 | 0 | 0fff | 907198 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 907198 | | a=58 x=3c y=3a | V_18=A; A=48; Y=00; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +2 | 0 | 0fff | 907198 | 6 | free cc: 16/59 | d018:48[10-55] d028:00[26-55] d02a:09[38-55] d02b:00[44-55] d02c:00[50-55] d011:3e[58-58] + | | | 907198 | +2 | a=48 x=3c y=00 | V_18=A; A=09; X=3e; N; N; V_28=Y; N; N; N; N; V_2a=A; A=38; V_2b=Y; N; V_2c=Y; Y=07; N; V_11=X +3 | 0 | 0fff | 009008 | 6 | free cc: 12/55 | d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:07[32-55] d02b:01[44-55] d02c:09[50-55] + | | | 009008 | | a=38 x=3e y=07 | V_18=A; V_11=A; X=01; N; V_28=A; A=09; V_29=Y; Y=28; N; N; N; V_2b=X; X=00; V_2c=A; N; N +4* | 0 | 0fff | 879198 | 4 | free cc: 27/59 | d029:09[10-25] d018:28[10-55] d02b:00[44-55] d011:3a[58-58] + | | | 899198 | | a=09 x=00 y=28 | V_29=A; V_18=Y; A=3a; Y=07; N; N; N; N; N; N; N; N; *N; V_2b=X; X=18; N; N; N; N; V_11=A +5 | 0 | 0fff | 899098 | 5 | free cc: 20/59 | d02a:07[10-31] d018:18[10-55] d028:09[26-55] d02a:09[38-55] d011:3c[58-58] + | | | 897098 | | a=3a x=18 y=07 | V_2a=Y; V_18=X; A=09; X=3c; V_28=A; Y=08; N; N; N; V_2a=A; A=07; N; N; N; N; N; N; N; V_11=X +6 | 0 | 0fff | 999098 | 9 | free cc: 4/60 | d02a:07[10-31] d02c:07[10-43] d018:08[10-55] d028:00[26-55] d029:00[32-55] d02b:07[44-55] d02c:09[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 997078 | | a=07 x=3c y=08 | V_2a=A; V_2c=A; V_18=Y; X=00; V_28=X; V_29=X; A=3e; Y=07; N; N; V_2b=Y; *Y=09; V_2c=Y; V_2d=X; V_11=A +7 | 0 | 0fff | 007790 | 9 | free cc: 0/56 | d02c:07[10-43] d018:78[10-55] d028:09[26-55] d011:38[10-55] d029:09[32-55] d02a:00[38-55] d02b:00[44-55] d02c:00[50-55] d02d:08[55-55] + | | | 007770 | | a=3e x=00 y=09 | A=07; V_2c=A; A=78; V_18=A; V_28=Y; A=38; V_11=A; V_29=Y; V_2a=X; V_2b=X; *Y=68; V_2c=X; V_2d=A +8* | 0 | 0fff | 990008 | 6 | free cc: 18/60 | d018:68[10-55] d029:00[32-55] d02a:09[38-55] d02b:07[44-55] d02d:00[55-55] d011:3a[58-58] + | | | 990008 | | a=38 x=00 y=68 | V_18=Y; A=3a; Y=09; N; N; N; N; *N; V_29=X; N; V_2a=Y; Y=07; V_2b=Y; Y=58; N; *N; V_2d=X; V_11=A +9 | 0 | 0fff | 909700 | 5 | free cc: 23/60 | d018:58[10-55] d029:09[32-55] d02a:00[38-55] d02d:09[55-55] d011:3c[58-58] + | | | 909700 | | a=3a x=00 y=58 | V_18=Y; A=3c; Y=09; N; N; N; N; N; V_29=Y; N; V_2a=X; X=48; N; N; N; N; *N; V_2d=Y; V_11=A +10 | 0 | 0fff | 990709 | 5 | free cc: 22/59 | d018:48[10-55] d02a:09[38-55] d02b:08[44-55] d02c:08[50-55] d011:3e[58-58] + | | | 990709 | | a=3c x=48 y=09 | V_18=X; A=3e; N; N; N; N; N; N; N; N; N; V_2a=Y; Y=00; V_2b=X; N; V_2c=X; X=38; N; V_11=A +11 | 0 | 0fff | 999889 | 5 | free cc: 21/56 | d02a:00[10-31] d018:38[10-55] d011:38[10-55] d02a:08[38-55] d02d:08[55-55] + | | | 990889 | | a=3e x=38 y=00 | V_2a=Y; V_18=X; V_11=X; A=28; Y=09; N; N; N; N; V_2a=A; N; N; N; N; N; *N; V_2d=A +12* | 0 | 0fff | 998888 | 6 | free cc: 17/59 | d02d:09[10-49] d018:28[10-55] d028:08[26-55] d02a:09[38-55] d02c:00[50-55] d011:3a[58-58] + | | | 998889 | +1 | a=28 x=38 y=09 | V_2d=Y; V_18=A; N; *N; V_28=A; A=00; X=3a; N; N; V_2a=Y; Y=18; N; N; N; V_2c=A; A=07; N; V_11=X +13 | 0 | 0fff | 899809 | 6 | free cc: 14/60 | d018:18[10-55] d029:00[32-55] d02b:07[44-55] d02c:08[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 899809 | | a=07 x=3a y=18 | V_18=Y; A=3c; X=08; Y=00; N; N; N; N; V_29=Y; Y=07; N; N; N; V_2b=Y; *Y=3e; V_2c=X; V_2d=X; V_11=A +14 | 0 | 0fff | 809788 | 4 | free cc: 24/59 | d018:08[10-55] d028:09[26-55] d029:07[32-55] d011:3e[58-58] + | | | 809788 | +1 | a=3c x=08 y=3e | V_18=X; A=09; X=07; N; N; V_28=A; A=08; V_29=X; X=78; N; N; N; N; N; N; N; N; N; N; V_11=Y +15 | 0 | 0fff | 979788 | 6 | free cc: 10/55 | d029:08[10-25] d018:78[10-55] d011:38[10-55] d029:03[32-55] d02a:00[38-55] d02b:00[44-55] + | | | 989788 | +1 | a=08 x=78 y=3e | V_29=A; V_18=X; A=38; V_11=A; A=03; X=00; V_29=A; A=68; V_2a=X; Y=09; V_2b=X; N; N; N; N; N +16* | 0 | 0fff | 930088 | 4 | free cc: 25/59 | d018:68[10-55] d02a:09[38-55] d02b:07[44-55] d011:3a[58-58] + | | | 930088 | | a=68 x=00 y=09 | V_18=A; A=07; X=3a; N; N; N; N; N; N; N; *N; V_2a=Y; Y=06; V_2b=A; A=58; N; N; N; N; V_11=X +17 | 0 | 0fff | 939788 | 6 | free cc: 14/59 | d02a:00[10-31] d02b:00[10-37] d02c:06[10-43] d018:58[10-55] d02c:01[50-55] d011:3c[58-58] + | | | 930068 | +2 | a=58 x=3a y=06 | X=00; V_2a=X; V_2b=X; V_2c=Y; V_18=A; A=01; X=3c; Y=03; N; N; N; N; N; N; V_2c=A; A=06; N; V_11=X +18 | 0 | 0fff | 930018 | 8 | free cc: 6/59 | d028:03[10-19] d02a:06[10-31] d018:48[10-55] d028:00[26-55] d029:00[32-55] d02a:00[38-55] d02c:00[50-55] d011:3e[58-58] + | | | 336018 | | a=06 x=3c y=03 | V_28=Y; V_2a=A; A=48; V_18=A; A=00; V_28=A; V_29=A; V_2a=A; X=3e; Y=09; N; N; V_2c=A; A=08; N; V_11=X +19 | 0 | 0fff | 000008 | 9 | free cc: 2/55 | d028:09[10-19] d029:08[10-25] d02a:07[10-31] d02b:07[10-37] d02c:08[10-43] d018:38[10-55] d011:38[10-55] d02a:08[38-55] d02b:01[44-55] + | | | 987788 | | a=08 x=3e y=09 | V_28=Y; V_29=A; X=07; V_2a=X; V_2b=X; V_2c=A; X=38; V_18=X; V_11=X; V_2a=A; A=01; V_2b=A; N +20* | 0 | 0fff | 988188 | 6 | free cc: 10/59 | d02b:00[10-37] d018:28[10-55] d028:03[26-55] d029:09[32-55] d02b:07[44-55] d011:3a[58-58] + | | | 988088 | | a=01 x=38 y=09 | A=00; V_2b=A; A=28; V_18=A; *A=03; V_28=A; V_29=Y; A=07; X=3a; Y=00; N; V_2b=A; A=18; N; N; N; N; V_11=X +21 | 0 | 0fff | 398788 | 6 | free cc: 16/59 | d029:00[10-25] d018:18[10-55] d028:08[26-55] d02b:00[44-55] d02c:09[50-55] d011:3c[58-58] + | | | 308788 | | a=18 x=3a y=00 | V_29=Y; V_18=A; X=09; N; V_28=A; A=3c; N; N; N; N; N; N; V_2b=Y; Y=06; V_2c=X; X=08; N; V_11=A +22 | 0 | 0fff | 808098 | 7 | free cc: 10/59 | d02c:06[10-43] d02d:00[10-49] d018:08[10-55] d028:01[26-55] d029:01[32-55] d02c:00[50-55] d011:3e[58-58] + | | | 808060 | +1 | a=3c x=08 y=06 | V_2c=Y; A=00; V_2d=A; V_18=X; X=01; V_28=X; V_29=X; X=3e; Y=08; N; N; N; N; V_2c=A; A=06; N; V_11=X +23 | 0 | 0fff | 118000 | 7 | free cc: 10/55 | d028:08[10-19] d029:06[10-25] d02c:01[10-43] d018:78[10-55] d029:08[32-55] d011:38[10-55] d02c:08[50-55] + | | | 868010 | +1 | a=06 x=3e y=08 | V_28=Y; V_29=A; A=01; V_2c=A; A=78; V_18=A; V_29=A; X=38; V_11=X; N; N; N; V_2c=A; N; N +24* | 0 | 0fff | 888080 | 7 | free cc: 9/60 | d018:68[10-55] d029:09[32-55] d02a:01[38-55] d02b:07[44-55] d02c:00[50-55] d02d:08[55-55] d011:3a[58-58] + | | | 888080 | | a=78 x=38 y=08 | A=68; V_18=A; A=3a; X=09; N; N; N; *N; V_29=X; X=01; V_2a=X; X=07; V_2b=X; *X=00; V_2c=X; V_2d=Y; V_11=A +25 | 0 | 0fff | 891708 | 8 | free cc: 2/59 | d029:01[10-25] d02a:07[10-31] d02c:09[10-43] d029:09[32-55] d018:58[10-55] d02a:08[38-55] d02c:08[50-55] d011:3c[58-58] + | | | 817798 | | a=3a x=00 y=08 | A=01; V_29=A; A=07; V_2a=A; A=09; V_2c=A; V_29=A; A=58; V_18=A; V_2a=A; X=3c; N; V_2c=A; A=01; Y=48; V_11=X +26 | 0 | 0fff | 898788 | 7 | free cc: 10/59 | d029:01[10-25] d018:48[10-55] d028:00[26-55] d029:08[32-55] d02b:00[44-55] d02c:06[50-55] d011:3e[58-58] + | | | 818788 | | a=01 x=3c y=48 | V_29=A; V_18=Y; A=00; X=06; V_28=A; N; V_29=Y; Y=3e; N; N; N; V_2b=A; A=08; V_2c=X; X=38; N; V_11=Y +27 | 0 | 0fff | 088068 | 8 | free cc: 4/55 | d02b:08[10-37] d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02b:01[44-55] d02c:08[50-55] + | | | 088868 | +1 | a=08 x=38 y=3e | V_2b=A; V_18=X; V_11=X; V_28=A; Y=01; V_29=Y; A=07; V_2a=A; A=28; V_2b=Y; Y=09; V_2c=A; N; N +28* | 0 | 0fff | 817188 | 7 | free cc: 10/60 | d018:28[10-55] d028:09[26-55] d029:08[32-55] d02a:08[38-55] d02b:07[44-55] d02d:00[55-55] d011:3a[58-58] + | | | 817188 | | a=28 x=38 y=09 | V_18=A; A=3a; Y=00; X=09; *N; V_28=X; X=08; V_29=X; N; V_2a=X; X=07; V_2b=X; X=18; N; *N; V_2d=Y; V_11=A +29 | 0 | 0fff | 988780 | 7 | free cc: 11/60 | d018:18[10-55] d028:00[26-55] d029:09[32-55] d02a:01[38-55] d02c:00[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 988780 | | a=3a x=18 y=00 | V_18=X; A=3c; X=08; N; N; V_28=Y; Y=09; V_29=Y; Y=01; V_2a=Y; Y=00; N; N; *N; V_2c=Y; V_2d=X; V_11=A +30 | 0 | 0fff | 091708 | 9 | free cc: 0/59 | d029:06[10-25] d02a:06[10-31] d018:08[10-55] d028:09[26-55] d029:08[32-55] d02a:01[38-55] d02b:00[44-55] d02c:08[50-55] d011:3e[58-58] + | | | 066708 | +1 | a=3c x=08 y=00 | A=06; V_29=A; V_2a=A; V_18=X; A=09; V_28=A; V_29=X; A=01; V_2a=A; V_2b=Y; A=3e; V_2c=X; X=78; Y=38; V_11=A +31 | 0 | 0fff | 981088 | 5 | free cc: 18/55 | d029:00[10-25] d018:78[10-55] d011:38[10-55] d029:08[32-55] d02a:07[38-55] + | | | 901088 | | a=3e x=78 y=38 | A=00; V_29=A; V_18=X; V_11=Y; A=07; N; V_29=X; X=06; V_2a=A; N; N; N; N; N; N; N; N +32* | 0 | 0fff | 987088 | 7 | free cc: 8/59 | d029:06[10-25] d018:68[10-55] d028:00[26-55] d029:01[32-55] d02a:00[38-55] d02b:08[44-55] d011:3a[58-58] + | | | 967088 | | a=07 x=06 y=38 | V_29=X; A=68; V_18=A; *X=00; V_28=X; A=01; V_29=A; A=3a; V_2a=X; X=58; V_2b=X; Y=00; N; N; N; N; V_11=A +33 | 0 | 0fff | 010888 | 5 | free cc: 22/59 | d018:58[10-55] d029:00[32-55] d02a:08[38-55] d02c:00[50-55] d011:3c[58-58] + | | | 010888 | | a=3a x=58 y=00 | V_18=X; A=3c; N; N; N; N; N; N; V_29=Y; N; V_2a=X; X=48; N; N; N; V_2c=Y; Y=01; N; V_11=A +34 | 0 | 0fff | 008808 | 7 | free cc: 11/60 | d018:48[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 008808 | | a=3c x=48 y=01 | V_18=X; A=3e; Y=00; N; N; V_28=X; X=01; V_29=X; X=07; V_2a=X; X=08; N; N; *N; V_2c=X; V_2d=Y; V_11=A +35 | 0 | 0fff | 817880 | 4 | free cc: 25/56 | d029:00[10-25] d018:38[10-55] d011:38[10-55] d02d:08[55-55] + | | | 807880 | | a=3e x=08 y=00 | V_29=Y; A=38; V_18=A; V_11=A; Y=28; N; N; N; N; N; N; N; N; N; N; N; *N; V_2d=A +36* | 0 | 0fff | 807888 | 3 | free cc: 29/59 | d018:28[10-55] d029:06[32-55] d011:3a[58-58] + | | | 807888 | | a=38 x=08 y=28 | V_18=Y; A=06; X=3a; Y=18; N; N; N; *N; V_29=A; A=00; N; N; N; N; N; N; N; N; N; N; V_11=X +37 | 0 | 0fff | 867888 | 6 | free cc: 16/59 | d02d:00[10-49] d018:18[10-55] d029:08[32-55] d02b:00[44-55] d02c:01[50-55] d011:3c[58-58] + | | | 867880 | +1 | a=00 x=3a y=18 | V_2d=A; V_18=Y; X=01; N; N; N; N; V_29=Y; Y=3c; N; N; N; V_2b=A; A=08; V_2c=X; X=3e; N; V_11=Y +38 | 0 | 0fff | 887010 | 6 | free cc: 18/59 | d02b:08[10-37] d018:08[10-55] d029:01[32-55] d02a:08[38-55] d02c:08[50-55] d011:3e[58-58] + | | | 887810 | | a=08 x=3e y=3c | V_2b=A; V_18=A; Y=01; N; N; N; N; V_29=Y; Y=07; V_2a=A; N; N; N; N; V_2c=A; A=78; N; V_11=X +39 | 0 | 0fff | 818880 | 7 | free cc: 10/55 | d02a:07[10-31] d02b:00[10-37] d018:78[10-55] d011:38[10-55] d029:00[32-55] d02a:00[38-55] d028:06[26-55] + | | | 817080 | +1 | a=78 x=3e y=07 | V_2a=Y; X=00; V_2b=X; V_18=A; A=38; V_11=A; V_29=X; V_2a=X; A=06; V_28=A; N; N; N; N; N +40* | 0 | 0fff | 600080 | 6 | free cc: 17/59 | d018:68[10-55] d028:08[26-55] d029:08[32-55] d02b:07[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 600080 | | a=06 x=00 y=07 | A=68; V_18=A; N; N; *N; V_28=A; N; V_29=A; A=3a; N; N; N; V_2b=Y; Y=58; V_2c=X; X=01; N; V_11=A +41 | 0 | 0fff | 880700 | 8 | free cc: 2/60 | d018:58[10-55] d028:00[26-55] d029:01[32-55] d02a:07[38-55] d02b:06[44-55] d02c:09[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 880700 | | a=3a x=01 y=58 | V_18=Y; A=3c; X=08; Y=00; N; V_28=Y; Y=01; V_29=Y; Y=07; V_2a=Y; Y=06; V_2b=Y; *Y=09; V_2c=Y; V_2d=X; V_11=A +42 | 0 | 0fff | 017698 | 6 | free cc: 14/59 | d02b:00[10-37] d018:48[10-55] d029:08[32-55] d02a:00[38-55] d02b:08[44-55] d011:3e[58-58] + | | | 017098 | | a=3c x=08 y=09 | A=00; V_2b=A; Y=48; V_18=Y; N; N; N; V_29=X; N; V_2a=A; A=3e; V_2b=X; X=38; Y=06; N; N; N; V_11=A +43 | 0 | 0fff | 080898 | 5 | free cc: 18/55 | d02c:08[10-43] d018:38[10-55] d011:38[10-55] d02b:06[44-55] d02c:01[50-55] + | | | 080888 | | a=3e x=38 y=06 | V_2c=X; V_18=X; V_11=X; A=01; X=28; N; N; N; N; N; N; N; V_2b=Y; Y=00; V_2c=A; N; N +44* | 0 | 0fff | 080618 | 6 | free cc: 17/59 | d02b:01[10-37] d018:28[10-55] d02a:06[38-55] d02b:00[44-55] d02c:08[50-55] d011:3a[58-58] + | | | 080118 | | a=01 x=28 y=00 | V_2b=A; V_18=X; A=06; N; N; N; N; N; N; *N; V_2a=A; A=3a; V_2b=Y; Y=18; V_2c=X; X=00; N; V_11=A +45 | 0 | 0fff | 086088 | 7 | free cc: 13/60 | d02a:00[10-31] d018:18[10-55] d029:09[32-55] d02a:06[38-55] d02c:00[50-55] d02d:09[55-55] d011:3c[58-58] + | | | 080088 | | a=3a x=00 y=18 | V_2a=X; V_18=Y; A=3c; Y=09; N; N; N; V_29=Y; X=06; V_2a=X; X=00; N; N; *N; V_2c=X; V_2d=Y; V_11=A +46 | 0 | 0fff | 096009 | 9 | free cc: 0/60 | d029:06[10-25] d02a:07[10-31] d028:09[26-55] d018:08[10-55] d029:08[32-55] d02b:07[44-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 067009 | | a=3c x=00 y=09 | A=06; V_29=A; A=07; V_2a=A; V_28=Y; Y=08; V_18=Y; V_29=Y; A=3e; Y=07; V_2b=Y; *Y=08; V_2c=Y; V_2d=X; V_11=A +47 | 0 | 0fff | 987780 | 7 | free cc: 3/56 | d02b:01[10-37] d018:78[10-55] d028:08[26-55] d011:38[10-55] d02a:00[38-55] d02b:07[44-55] d02d:09[55-55] + | | | 987180 | | a=3e x=00 y=08 | A=01; V_2b=A; A=78; V_18=A; V_28=A; A=38; V_11=A; A=07; V_2a=X; X=09; V_2b=A; A=68; Y=00; *N; V_2d=X +48* | 0 | 0fff | 880789 | 8 | free cc: 12/60 | d018:68[10-55] d028:00[26-55] d029:00[32-55] d02a:07[38-55] d02b:00[44-55] d02c:00[50-55] d02d:00[55-55] d011:3a[58-58] + | | | 880789 | | a=68 x=09 y=00 | V_18=A; A=3a; X=07; N; *N; V_28=Y; N; V_29=Y; N; V_2a=X; X=01; V_2b=Y; *N; V_2c=Y; V_2d=Y; V_11=A +49 | 0 | 0fff | 007000 | 6 | free cc: 16/59 | d02a:01[10-31] d018:58[10-55] d029:01[32-55] d02a:08[38-55] d02c:08[50-55] d011:3c[58-58] + | | | 001000 | | a=3a x=01 y=00 | V_2a=X; A=58; V_18=A; Y=3c; N; N; N; V_29=X; X=48; V_2a=A; N; N; N; N; V_2c=A; A=09; N; V_11=Y +50 | 0 | 0fff | 018080 | 5 | free cc: 18/59 | d018:48[10-55] d028:09[26-55] d029:00[32-55] d02a:07[38-55] d011:3e[58-58] + | | | 018080 | | a=09 x=48 y=3c | V_18=X; X=00; Y=07; N; N; V_28=A; A=3e; V_29=X; X=38; V_2a=Y; Y=01; N; N; N; N; N; N; N; V_11=A +51 | 0 | 0fff | 907080 | 6 | free cc: 16/55 | d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:01[32-55] d02a:08[38-55] d02b:08[44-55] + | | | 907080 | | a=3e x=38 y=01 | V_18=X; V_11=X; A=28; N; V_28=A; N; V_29=Y; Y=09; V_2a=A; N; V_2b=A; N; N; N; N; N +52* | 0 | 0fff | 818880 | 6 | free cc: 15/59 | d018:28[10-55] d028:09[26-55] d029:08[32-55] d02a:00[38-55] d02c:01[50-55] d011:3a[58-58] + | | | 818880 | | a=28 x=38 y=09 | V_18=A; N; N; N; *N; V_28=Y; Y=00; V_29=A; A=01; V_2a=Y; X=3a; Y=18; N; N; V_2c=A; A=07; N; V_11=X +53 | 0 | 0fff | 980810 | 7 | free cc: 11/60 | d018:18[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02c:00[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 980810 | | a=07 x=3a y=18 | V_18=Y; A=3c; X=08; N; N; V_28=X; Y=01; V_29=Y; Y=07; V_2a=Y; Y=00; N; N; *N; V_2c=Y; V_2d=X; V_11=A +54 | 0 | 0fff | 817808 | 4 | free cc: 31/60 | d018:08[10-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 817808 | +1 | a=3c x=08 y=00 | V_18=X; A=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_2c=X; V_2d=Y; V_11=A +55 | 0 | 0fff | 817880 | 5 | free cc: 21/56 | d018:78[10-55] d011:38[10-55] d029:08[32-55] d02a:08[38-55] d02d:08[55-55] + | | | 817880 | | a=3e x=08 y=00 | A=78; V_18=A; Y=38; V_11=Y; N; N; N; V_29=A; N; V_2a=A; N; N; N; N; N; *N; V_2d=A +56* | 0 | 0fff | 888888 | 8 | free cc: 0/59 | d02d:00[10-49] d018:68[10-55] d028:09[26-55] d029:06[32-55] d02a:07[38-55] d02b:01[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 888880 | +1 | a=78 x=08 y=38 | A=00; V_2d=A; X=68; V_18=X; X=09; V_28=X; X=06; V_29=X; X=07; V_2a=X; *X=01; V_2b=X; V_2c=A; A=3a; X=58; V_11=A +57 | 0 | 0fff | 967100 | 5 | free cc: 20/59 | d018:58[10-55] d028:00[26-55] d029:00[32-55] d02c:08[50-55] d011:3c[58-58] + | | | 967100 | | a=3a x=58 y=38 | V_18=X; A=00; N; N; N; V_28=A; N; V_29=A; A=3c; N; N; N; N; N; N; V_2c=X; X=00; Y=48; V_11=A +58 | 0 | 0fff | 007180 | 7 | free cc: 10/59 | d02b:00[10-37] d018:48[10-55] d028:09[26-55] d029:01[32-55] d02a:08[38-55] d02b:08[44-55] d011:3e[58-58] + | | | 007080 | | a=3c x=00 y=48 | V_2b=X; V_18=Y; A=09; X=01; V_28=A; A=3e; V_29=X; X=38; V_2a=X; N; V_2b=X; Y=00; N; N; N; N; V_11=A +59 | 0 | 0fff | 918880 | 6 | free cc: 12/55 | d018:38[10-55] d011:38[10-55] d028:00[26-55] d029:00[32-55] d02a:07[38-55] d02b:01[44-55] + | | | 918880 | | a=3e x=38 y=00 | V_18=X; V_11=X; A=07; X=01; V_28=Y; N; V_29=Y; Y=28; V_2a=A; A=00; V_2b=X; N; N; N; N; N +60* | 0 | 0fff | 007180 | 5 | free cc: 23/59 | d018:28[10-55] d028:08[26-55] d029:08[32-55] d02b:00[44-55] d011:3a[58-58] + | | | 007180 | | a=00 x=01 y=28 | V_18=Y; X=3a; N; N; *N; V_28=Y; N; V_29=Y; Y=18; N; N; N; V_2b=A; A=09; N; N; N; N; V_11=X +61 | 0 | 0fff | 887080 | 10 | free cc: 0/60 | d02b:08[10-37] d018:18[10-55] d001:d4[10-55] d003:d4[10-55] d005:d4[10-55] d007:d4[10-55] d009:d4[10-55] d00b:d4[10-55] d02d:09[55-55] d011:3c[58-58] + | | | 887880 | +6 | a=09 x=3a y=18 | V_2b=Y; V_18=Y; X=d4; V_01=X; V_03=X; V_05=X; V_07=X; V_09=X; V_0b=X; A=3c; X=09; *Y=08; V_2d=X; V_11=A +62 | 0 | 0fff | 887889 | 9 | free cc: 6/60 | d018:08[10-55] d00d:d4[10-55] d00f:d4[10-55] d028:09[26-55] d02a:08[38-55] d02b:09[44-55] d02c:00[50-55] d02d:08[55-55] d011:3e[58-58] + | | | 887889 | +2 | a=3c x=09 y=08 | V_18=Y; A=d4; V_0d=A; V_0f=A; V_28=X; A=3e; N; N; V_2a=Y; N; V_2b=X; *X=00; V_2c=X; V_2d=Y; V_11=A +63 | 0 | 0fff | 988908 | 7 | free cc: 8/56 | d02b:00[10-37] dd00:ffffff03[10-55] d011:38[10-55] d028:08[26-55] d02b:07[44-55] d02c:08[50-55] d02d:09[55-55] + | | | 988008 | | a=3e x=00 y=08 | V_2b=X; A=ffffff03; V_00=A; A=38; V_11=A; V_28=A; X=07; N; N; N; N; V_2b=X; *X=09; V_2c=A; V_2d=X +64* | 0 | 0fff | 888789 | 8 | free cc: 4/59 | d02d:00[10-49] d018:98[10-55] d028:09[26-55] d029:09[32-55] d02a:01[38-55] d02b:01[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 888780 | +1 | a=38 x=09 y=08 | A=00; V_2d=A; *Y=98; V_18=Y; V_28=X; Y=01; V_29=X; X=3a; V_2a=Y; N; V_2b=Y; Y=08; V_2c=A; A=a8; N; V_11=X +65 | 0 | 0fff | 991100 | 8 | free cc: 6/59 | d02b:08[10-37] d02c:08[10-43] d018:a8[10-55] d029:08[32-55] d02a:00[38-55] d02b:00[44-55] d02c:06[50-55] d011:3c[58-58] + | | | 991880 | | a=a8 x=3a y=08 | V_2b=A; V_2c=A; V_18=A; X=00; N; N; V_29=A; A=06; V_2a=X; Y=3c; V_2b=X; X=b8; V_2c=A; A=09; N; V_11=Y +66 | 0 | 0fff | 980060 | 6 | free cc: 16/59 | d018:b8[10-55] d029:09[32-55] d02a:08[38-55] d02b:09[44-55] d02c:01[50-55] d011:3e[58-58] + | | | 980060 | +1 | a=09 x=b8 y=3c | V_18=X; Y=01; N; N; N; N; N; N; V_29=A; N; V_2a=X; X=3e; V_2b=A; A=08; V_2c=Y; Y=88; N; V_11=X +67 | 0 | 0fff | 998910 | 8 | free cc: 3/56 | d028:08[10-19] d02b:07[10-37] d018:88[10-55] d011:38[10-55] d028:00[26-55] d02a:00[38-55] d02c:00[50-55] d02d:06[55-55] + | | | 898710 | +2 | a=08 x=3e y=88 | V_28=A; A=07; V_2b=A; V_18=Y; A=38; V_11=A; A=00; V_28=A; V_2a=A; X=06; Y=07; *N; V_2c=A; V_2d=X +68* | 0 | 0fff | 090706 | 6 | free cc: 15/59 | d028:08[10-19] d02a:07[10-31] d02c:09[10-43] d018:98[10-55] d028:09[26-55] d011:3a[58-58] + | | | 897796 | | a=00 x=06 y=07 | A=98; V_28=A; V_2a=Y; X=09; V_2c=X; V_18=A; V_28=X; A=3a; X=00; Y=a8; N; N; N; N; N; N; *N; V_11=A +69 | 0 | 0fff | 997796 | 8 | free cc: 6/59 | d028:00[10-19] d02a:00[10-31] d02c:00[10-43] d018:a8[10-55] d028:09[26-55] d02a:08[38-55] d02c:06[50-55] d011:3c[58-58] + | | | 090706 | | a=3a x=00 y=a8 | V_28=X; V_2a=X; V_2c=X; V_18=Y; A=09; V_28=A; A=06; V_2a=Y; X=3c; Y=07; N; N; V_2c=A; A=00; N; V_11=X +70 | 0 | 0fff | 998766 | 7 | free cc: 10/59 | d02a:07[10-31] d02c:00[10-43] d02d:00[10-49] d018:b8[10-55] d02a:00[38-55] d02c:09[50-55] d011:3e[58-58] + | | | 997700 | | a=00 x=3c y=07 | V_2a=Y; V_2c=A; V_2d=A; X=b8; V_18=X; X=09; Y=3e; N; V_2a=A; A=08; N; N; N; V_2c=X; X=06; N; V_11=Y +71 | 0 | 0fff | 990790 | 8 | free cc: 3/56 | d02c:08[10-43] d02d:06[10-49] d018:88[10-55] d011:38[10-55] d028:01[26-55] d02a:07[38-55] d02c:06[50-55] d02d:01[55-55] + | | | 990786 | | a=08 x=06 y=3e | V_2c=A; V_2d=X; A=88; V_18=A; A=38; V_11=A; A=01; V_28=A; Y=07; V_2a=Y; Y=08; *N; V_2c=X; V_2d=A +72* | 0 | 0fff | 197761 | 6 | free cc: 13/59 | d028:08[10-19] d02c:00[10-43] d018:98[10-55] d02a:00[38-55] d02c:09[50-55] d011:3a[58-58] + | | | 897701 | | a=01 x=06 y=08 | V_28=Y; A=00; V_2c=A; X=98; V_18=X; X=09; Y=3a; N; *N; V_2a=A; A=a8; N; N; N; V_2c=X; X=3c; N; V_11=Y +73 | 0 | 0fff | 890791 | 7 | free cc: 15/60 | d02d:09[10-49] d018:a8[10-55] d029:08[32-55] d02a:08[38-55] d02c:08[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 890799 | | a=a8 x=3c y=3a | Y=09; V_2d=Y; V_18=A; A=3c; Y=08; N; N; V_29=Y; N; V_2a=Y; N; N; N; *N; V_2c=Y; V_2d=Y; V_11=A +74 | 0 | 0fff | 888788 | 6 | free cc: 17/60 | d018:b8[10-55] d028:00[26-55] d029:09[32-55] d02c:09[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 888788 | | a=3c x=3c y=08 | A=b8; V_18=A; A=3e; X=00; Y=09; V_28=X; N; V_29=Y; N; N; N; N; N; N; *N; V_2c=Y; V_2d=X; V_11=A +75 | 0 | 0fff | 098790 | 7 | free cc: 10/56 | d018:88[10-55] d011:38[10-55] d028:08[26-55] d029:00[32-55] d02b:00[44-55] d02c:08[50-55] d02d:08[55-55] + | | | 098790 | | a=3e x=00 y=09 | A=88; V_18=A; Y=38; V_11=Y; V_28=A; N; V_29=X; N; N; N; N; V_2b=X; *X=98; V_2c=A; V_2d=A +76* | 0 | 0fff | 808088 | 6 | free cc: 15/60 | d018:98[10-55] d02a:07[38-55] d02b:08[44-55] d02c:00[50-55] d02d:00[55-55] d011:3a[58-58] + | | | 808088 | | a=88 x=98 y=38 | V_18=X; A=3a; X=00; Y=07; N; N; N; N; N; N; *N; V_2a=Y; Y=08; V_2b=Y; *Y=09; V_2c=X; V_2d=X; V_11=A +77 | 0 | 0fff | 807800 | 6 | free cc: 15/60 | d029:09[10-25] d018:a8[10-55] d029:00[32-55] d02b:01[44-55] d02d:08[55-55] d011:3c[58-58] + | | | 897800 | +1 | a=3a x=00 y=09 | V_29=Y; A=a8; V_18=A; A=3c; Y=08; N; N; V_29=X; X=01; N; N; N; V_2b=X; X=b8; N; *N; V_2d=X; V_11=A +78 | 0 | 0fff | 807108 | 7 | free cc: 6/59 | d018:b8[10-55] d028:00[26-55] d029:03[32-55] d02a:00[38-55] d02b:08[44-55] d02c:09[50-55] d011:3e[58-58] + | | | 807108 | | a=3c x=b8 y=08 | V_18=X; A=00; N; N; N; V_28=A; A=03; V_29=A; A=00; V_2a=A; A=09; V_2b=X; X=3e; V_2c=A; A=00; Y=88; V_11=X +79 | 0 | 0fff | 030898 | 6 | free cc: 16/55 | d028:08[10-19] d029:00[10-25] d02c:00[10-43] d018:88[10-55] d011:38[10-55] d02a:07[38-55] + | | | 800808 | +2 | a=00 x=3e y=88 | V_28=Y; V_29=A; V_2c=A; V_18=Y; A=38; V_11=A; A=07; V_2a=A; N; N; N; N; N; N; N; N +80* | 0 | 0fff | 807808 | 6 | free cc: 13/59 | d029:08[10-25] d02a:01[10-31] d018:98[10-55] d02a:08[38-55] d02b:00[44-55] d011:3a[58-58] + | | | 881808 | | a=07 x=3e y=88 | V_29=Y; A=01; V_2a=A; A=98; V_18=A; X=00; N; N; *N; V_2a=A; A=3a; V_2b=X; X=03; Y=06; N; N; N; V_11=A +81 | 0 | 0fff | 888008 | 9 | free cc: 0/59 | d028:03[10-19] d029:03[10-25] d02a:06[10-31] d02b:03[10-37] d02c:03[10-43] d018:a8[10-55] d028:09[26-55] d02a:00[38-55] d011:3c[58-58] + | | | 336338 | +1 | a=3a x=03 y=06 | V_28=X; V_29=X; V_2a=Y; V_2b=X; V_2c=X; A=a8; V_18=A; A=09; V_28=A; A=00; V_2a=A; A=3c; X=b8; Y=03; V_11=A +82 | 0 | 0fff | 930338 | 5 | free cc: 22/59 | d018:b8[10-55] d029:08[32-55] d02a:03[38-55] d02c:08[50-55] d011:3e[58-58] + | | | 930338 | | a=3c x=b8 y=03 | V_18=X; A=3e; N; N; N; N; N; N; V_29=X; N; V_2a=Y; Y=98; N; N; N; V_2c=X; X=38; N; V_11=A +83 | 0 | 0fff | 983388 | 3 | free cc: 26/55 | d018:98[10-55] d011:38[10-55] d02c:03[50-55] + | | | 983388 | | a=3e x=38 y=98 | V_18=Y; V_11=X; A=03; X=09; Y=a8; N; N; N; N; N; N; N; N; N; N; N; V_2c=A; N; N +84* | 0 | 0fff | 983338 | 8 | free cc: 8/59 | d02d:09[10-49] d018:a8[10-55] d028:01[26-55] d029:01[32-55] d02a:01[38-55] d02b:01[44-55] d02c:01[50-55] d011:3a[58-58] + | | | 983339 | | a=03 x=09 y=a8 | V_2d=X; V_18=Y; A=01; *X=3a; V_28=A; Y=09; V_29=A; N; V_2a=A; N; V_2b=A; N; V_2c=A; A=06; N; V_11=X +85 | 0 | 0fff | 111119 | 10 | free cc: 0/60 | d028:08[10-19] d029:00[10-25] d02a:00[10-31] d02b:08[10-37] d02c:09[10-43] d018:b8[10-55] d02a:07[38-55] d02b:00[44-55] d02d:00[55-55] d011:3c[58-58] + | | | 800899 | | a=06 x=3a y=09 | A=b8; V_28=A; X=00; V_29=X; V_2a=X; V_2b=A; V_2c=Y; V_18=A; A=07; V_2a=A; V_2b=X; *A=3c; V_2d=X; V_11=A +86 | 0 | 0fff | 807090 | 7 | free cc: 10/59 | d029:07[10-25] d018:88[10-55] d028:00[26-55] d029:08[32-55] d02a:00[38-55] d02c:00[50-55] d011:3e[58-58] + | | | 877090 | | a=3c x=00 y=09 | A=07; V_29=A; A=88; V_18=A; V_28=X; Y=3e; V_29=A; A=07; V_2a=X; N; N; N; N; V_2c=X; X=98; N; V_11=Y +87 | 0 | 0fff | 080000 | 5 | free cc: 20/55 | d02a:07[10-31] d018:98[10-55] d011:38[10-55] d028:08[26-55] d029:00[32-55] + | | | 087000 | | a=07 x=98 y=3e | V_2a=A; V_18=X; A=38; V_11=A; V_28=A; A=00; V_29=A; N; N; N; N; N; N; N; N; N; N +88* | 0 | 0fff | 807000 | 6 | free cc: 16/60 | d018:a8[10-55] d029:07[32-55] d02a:08[38-55] d02b:07[44-55] d02d:08[55-55] d011:3a[58-58] + | | | 807000 | | a=00 x=98 y=3e | A=a8; V_18=A; A=3a; Y=08; X=07; N; N; *N; V_29=X; N; V_2a=Y; N; V_2b=X; X=00; N; *N; V_2d=Y; V_11=A +89 | 0 | 0fff | 878708 | 5 | free cc: 20/59 | d02d:00[10-49] d018:b8[10-55] d028:00[26-55] d02a:00[38-55] d011:3c[58-58] + | | | 878700 | | a=3a x=00 y=08 | V_2d=X; A=b8; V_18=A; A=3c; V_28=X; Y=09; N; N; N; V_2a=X; X=88; N; N; N; N; N; N; N; V_11=A +90 | 0 | 0fff | 070700 | 5 | free cc: 25/60 | d029:09[10-25] d018:88[10-55] d029:08[32-55] d02d:08[55-55] d011:3e[58-58] + | | | 090700 | | a=3c x=88 y=09 | V_29=Y; V_18=X; A=3e; Y=08; N; N; N; V_29=X; N; N; N; N; N; N; N; N; *N; V_2d=X; V_11=A +91 | 0 | 0fff | 080708 | 5 | free cc: 17/56 | d018:98[10-55] d011:38[10-55] d029:07[32-55] d02c:08[50-55] d02d:00[55-55] + | | | 080708 | | a=3e x=88 y=08 | A=98; V_18=A; A=38; V_11=A; A=07; N; N; V_29=A; A=00; N; N; N; N; N; *N; V_2c=X; V_2d=A +92* | 0 | 0fff | 070780 | 6 | free cc: 14/60 | d029:09[10-25] d02b:01[10-37] d018:a8[10-55] d02b:00[44-55] d02d:08[55-55] d011:3a[58-58] + | | | 090180 | | a=00 x=88 y=08 | A=09; V_29=A; A=01; V_2b=A; A=a8; V_18=A; A=3a; X=00; N; N; N; *N; V_2b=X; X=07; N; *N; V_2d=Y; V_11=A +93 | 0 | 0fff | 090088 | 5 | free cc: 18/59 | d029:07[10-25] d018:b8[10-55] d029:00[32-55] d02a:07[38-55] d011:3c[58-58] + | | | 070088 | | a=3a x=07 y=08 | V_29=X; A=b8; V_18=A; A=00; Y=3c; N; N; V_29=A; A=88; V_2a=X; X=00; N; N; N; N; N; N; N; V_11=Y +94 | 0 | 0fff | 007088 | 5 | free cc: 20/59 | d018:88[10-55] d029:08[32-55] d02a:00[38-55] d02b:07[44-55] d011:3e[58-58] + | | | 007088 | | a=88 x=00 y=3c | V_18=A; Y=07; N; N; N; N; N; N; V_29=A; A=3e; V_2a=X; X=98; V_2b=Y; Y=38; N; N; N; N; V_11=A +95 | 0 | 0fff | 080788 | 5 | free cc: 16/55 | d018:98[10-55] d011:38[10-55] d029:00[32-55] d02a:07[38-55] d02c:00[50-55] + | | | 080788 | | a=3e x=98 y=38 | V_18=X; V_11=Y; A=00; X=07; Y=a8; N; N; V_29=A; N; V_2a=X; X=3a; N; N; N; V_2c=A; N; N +96* | 0 | 0fff | 007708 | 2 | free cc: 37/59 | d018:a8[10-55] d011:3a[58-58] + | | | 007708 | | a=00 x=3a y=a8 | V_18=Y; A=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=X +97 | 0 | 0fff | 007708 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 007708 | | a=b8 x=3a y=3c | V_18=A; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +98 | 0 | 0fff | 007708 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 007708 | | a=88 x=3e y=3c | V_18=A; A=18; Y=10; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +99 | 0 | 0fff | 007708 | 2 | free cc: 38/57 | d018:18[10-55] d011:10[56-56] + | | | | | a=18 x=3e y=10 | V_18=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +free cycles = 1461 + +Resolutions: +Included 2 UnderlayColor d02c:00/32-37/6-7 +Included 2 UnderlayColor d02a:09/26-37/6-9 +Included 12 UnderlayColor d02d:09/0a-31/25-27 +Included 14 UnderlayColor d028:09/1a-37/30-36 +Included 15 UnderlayColor d029:03/20-37/32-37 +Included 17 UnderlayColor d02a:00/0a-1f/35-36 +Included 17 UnderlayColor d02b:00/0a-25/35-37 +Included 22 UnderlayColor d02d:00/0a-31/45-48 +Included 23 UnderlayColor d02c:08/32-37/48-49 +Included 27 UnderlayColor d029:01/20-37/56-57 +Included 30 UnderlayColor d02b:00/2c-37/62-64 +Included 37 UnderlayColor d02d:00/0a-31/75-82 +Included 39 UnderlayColor d02b:00/0a-25/79-80 +Included 54 UnderlayColor d02d:00/37-37/110-111 +Included 56 UnderlayColor d02d:00/0a-31/113-114 +Included 61 SpriteY d001:d4/0a-37/123-149 +Included 61 SpriteY d003:d4/0a-37/123-151 +Included 61 SpriteY d005:d4/0a-37/123-153 +Included 61 SpriteY d007:d4/0a-37/123-155 +Included 61 SpriteY d009:d4/0a-37/123-157 +Included 61 SpriteY d00b:d4/0a-37/123-159 +Included 62 SpriteY d00d:d4/0a-37/123-161 +Included 62 SpriteY d00f:d4/0a-37/123-163 +Included 64 UnderlayColor d02d:00/0a-31/129-135 +Included 66 UnderlayColor d029:09/20-37/134-147 +Included 67 UnderlayColor d02b:07/0a-25/135-137 +Included 67 UnderlayColor d02d:06/37-37/136-140 +Included 77 UnderlayColor d02d:08/37-37/156-166 +Included 79 UnderlayColor d02c:00/0a-2b/159-160 +Included 79 UnderlayColor d028:08/0a-13/159-162 +Included 81 UnderlayColor d029:03/0a-19/163-164 +Overflow 85 (67): Overriding Underlay 85 6 d028:09/0a-13/171-171 -> 08 +Overflow 85 (63): Overriding Underlay 85 7 d029:06/0a-19/171-171 -> 00 diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.png new file mode 100644 index 0000000..b3f72bf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.prg b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.prg new file mode 100644 index 0000000..d029e3b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-prepared.prg differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-result.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-result.png new file mode 100644 index 0000000..958b453 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-result.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-speedcode.txt b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-speedcode.txt new file mode 100644 index 0000000..3caae27 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320-speedcode.txt @@ -0,0 +1,236 @@ +0* | 0 | 0fff | | 2 | free cc: 36/59 | d018:68[10-55] d011:3a[58-58] + | | | 907198 | | a=38 x=68 y=3a | V_18=X; A=58; X=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +1 | 0 | 0fff | 907198 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 907198 | | a=58 x=3c y=3a | V_18=A; A=48; Y=00; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +2 | 0 | 0fff | 907198 | 6 | free cc: 16/59 | d018:48[10-55] d028:00[26-55] d02a:09[38-55] d02b:00[44-55] d02c:00[50-55] d011:3e[58-58] + | | | 907198 | +2 | a=48 x=3c y=00 | V_18=A; A=09; X=3e; N; N; V_28=Y; N; N; N; N; V_2a=A; A=38; V_2b=Y; N; V_2c=Y; Y=07; N; V_11=X +3 | 0 | 0fff | 009008 | 6 | free cc: 12/55 | d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:07[32-55] d02b:01[44-55] d02c:09[50-55] + | | | 009008 | | a=38 x=3e y=07 | V_18=A; V_11=A; X=01; N; V_28=A; A=09; V_29=Y; Y=28; N; N; N; V_2b=X; X=00; V_2c=A; N; N +4* | 0 | 0fff | 879198 | 4 | free cc: 27/59 | d029:09[10-25] d018:28[10-55] d02b:00[44-55] d011:3a[58-58] + | | | 899198 | | a=09 x=00 y=28 | V_29=A; V_18=Y; A=3a; Y=07; N; N; N; N; N; N; N; N; *N; V_2b=X; X=18; N; N; N; N; V_11=A +5 | 0 | 0fff | 899098 | 5 | free cc: 20/59 | d02a:07[10-31] d018:18[10-55] d028:09[26-55] d02a:09[38-55] d011:3c[58-58] + | | | 897098 | | a=3a x=18 y=07 | V_2a=Y; V_18=X; A=09; X=3c; V_28=A; Y=08; N; N; N; V_2a=A; A=07; N; N; N; N; N; N; N; V_11=X +6 | 0 | 0fff | 999098 | 9 | free cc: 4/60 | d02a:07[10-31] d02c:07[10-43] d018:08[10-55] d028:00[26-55] d029:00[32-55] d02b:07[44-55] d02c:09[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 997078 | | a=07 x=3c y=08 | V_2a=A; V_2c=A; V_18=Y; X=00; V_28=X; V_29=X; A=3e; Y=07; N; N; V_2b=Y; *Y=09; V_2c=Y; V_2d=X; V_11=A +7 | 0 | 0fff | 007790 | 9 | free cc: 0/56 | d02c:07[10-43] d018:78[10-55] d028:09[26-55] d011:38[10-55] d029:09[32-55] d02a:00[38-55] d02b:00[44-55] d02c:00[50-55] d02d:08[55-55] + | | | 007770 | | a=3e x=00 y=09 | A=07; V_2c=A; A=78; V_18=A; V_28=Y; A=38; V_11=A; V_29=Y; V_2a=X; V_2b=X; *Y=68; V_2c=X; V_2d=A +8* | 0 | 0fff | 990008 | 6 | free cc: 18/60 | d018:68[10-55] d029:00[32-55] d02a:09[38-55] d02b:07[44-55] d02d:00[55-55] d011:3a[58-58] + | | | 990008 | | a=38 x=00 y=68 | V_18=Y; A=3a; Y=09; N; N; N; N; *N; V_29=X; N; V_2a=Y; Y=07; V_2b=Y; Y=58; N; *N; V_2d=X; V_11=A +9 | 0 | 0fff | 909700 | 5 | free cc: 23/60 | d018:58[10-55] d029:09[32-55] d02a:00[38-55] d02d:09[55-55] d011:3c[58-58] + | | | 909700 | | a=3a x=00 y=58 | V_18=Y; A=3c; Y=09; N; N; N; N; N; V_29=Y; N; V_2a=X; X=48; N; N; N; N; *N; V_2d=Y; V_11=A +10 | 0 | 0fff | 990709 | 5 | free cc: 22/59 | d018:48[10-55] d02a:09[38-55] d02b:08[44-55] d02c:08[50-55] d011:3e[58-58] + | | | 990709 | | a=3c x=48 y=09 | V_18=X; A=3e; N; N; N; N; N; N; N; N; N; V_2a=Y; Y=00; V_2b=X; N; V_2c=X; X=38; N; V_11=A +11 | 0 | 0fff | 999889 | 5 | free cc: 21/56 | d02a:00[10-31] d018:38[10-55] d011:38[10-55] d02a:08[38-55] d02d:08[55-55] + | | | 990889 | | a=3e x=38 y=00 | V_2a=Y; V_18=X; V_11=X; A=28; Y=09; N; N; N; N; V_2a=A; N; N; N; N; N; *N; V_2d=A +12* | 0 | 0fff | 998888 | 6 | free cc: 17/59 | d02d:09[10-49] d018:28[10-55] d028:08[26-55] d02a:09[38-55] d02c:00[50-55] d011:3a[58-58] + | | | 998889 | +1 | a=28 x=38 y=09 | V_2d=Y; V_18=A; N; *N; V_28=A; A=00; X=3a; N; N; V_2a=Y; Y=18; N; N; N; V_2c=A; A=07; N; V_11=X +13 | 0 | 0fff | 899809 | 6 | free cc: 14/60 | d018:18[10-55] d029:00[32-55] d02b:07[44-55] d02c:08[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 899809 | | a=07 x=3a y=18 | V_18=Y; A=3c; X=08; Y=00; N; N; N; N; V_29=Y; Y=07; N; N; N; V_2b=Y; *Y=3e; V_2c=X; V_2d=X; V_11=A +14 | 0 | 0fff | 809788 | 4 | free cc: 24/59 | d018:08[10-55] d028:09[26-55] d029:07[32-55] d011:3e[58-58] + | | | 809788 | +1 | a=3c x=08 y=3e | V_18=X; A=09; X=07; N; N; V_28=A; A=08; V_29=X; X=78; N; N; N; N; N; N; N; N; N; N; V_11=Y +15 | 0 | 0fff | 979788 | 6 | free cc: 10/55 | d029:08[10-25] d018:78[10-55] d011:38[10-55] d029:03[32-55] d02a:00[38-55] d02b:00[44-55] + | | | 989788 | +1 | a=08 x=78 y=3e | V_29=A; V_18=X; A=38; V_11=A; A=03; X=00; V_29=A; A=68; V_2a=X; Y=09; V_2b=X; N; N; N; N; N +16* | 0 | 0fff | 930088 | 4 | free cc: 25/59 | d018:68[10-55] d02a:09[38-55] d02b:07[44-55] d011:3a[58-58] + | | | 930088 | | a=68 x=00 y=09 | V_18=A; A=07; X=3a; N; N; N; N; N; N; N; *N; V_2a=Y; Y=06; V_2b=A; A=58; N; N; N; N; V_11=X +17 | 0 | 0fff | 939788 | 6 | free cc: 14/59 | d02a:00[10-31] d02b:00[10-37] d02c:06[10-43] d018:58[10-55] d02c:01[50-55] d011:3c[58-58] + | | | 930068 | +2 | a=58 x=3a y=06 | X=00; V_2a=X; V_2b=X; V_2c=Y; V_18=A; A=01; X=3c; Y=03; N; N; N; N; N; N; V_2c=A; A=06; N; V_11=X +18 | 0 | 0fff | 930018 | 8 | free cc: 6/59 | d028:03[10-19] d02a:06[10-31] d018:48[10-55] d028:00[26-55] d029:00[32-55] d02a:00[38-55] d02c:00[50-55] d011:3e[58-58] + | | | 336018 | | a=06 x=3c y=03 | V_28=Y; V_2a=A; A=48; V_18=A; A=00; V_28=A; V_29=A; V_2a=A; X=3e; Y=09; N; N; V_2c=A; A=08; N; V_11=X +19 | 0 | 0fff | 000008 | 9 | free cc: 2/55 | d028:09[10-19] d029:08[10-25] d02a:07[10-31] d02b:07[10-37] d02c:08[10-43] d018:38[10-55] d011:38[10-55] d02a:08[38-55] d02b:01[44-55] + | | | 987788 | | a=08 x=3e y=09 | V_28=Y; V_29=A; X=07; V_2a=X; V_2b=X; V_2c=A; X=38; V_18=X; V_11=X; V_2a=A; A=01; V_2b=A; N +20* | 0 | 0fff | 988188 | 6 | free cc: 10/59 | d02b:00[10-37] d018:28[10-55] d028:03[26-55] d029:09[32-55] d02b:07[44-55] d011:3a[58-58] + | | | 988088 | | a=01 x=38 y=09 | A=00; V_2b=A; A=28; V_18=A; *A=03; V_28=A; V_29=Y; A=07; X=3a; Y=00; N; V_2b=A; A=18; N; N; N; N; V_11=X +21 | 0 | 0fff | 398788 | 6 | free cc: 16/59 | d029:00[10-25] d018:18[10-55] d028:08[26-55] d02b:00[44-55] d02c:09[50-55] d011:3c[58-58] + | | | 308788 | | a=18 x=3a y=00 | V_29=Y; V_18=A; X=09; N; V_28=A; A=3c; N; N; N; N; N; N; V_2b=Y; Y=06; V_2c=X; X=08; N; V_11=A +22 | 0 | 0fff | 808098 | 7 | free cc: 10/59 | d02c:06[10-43] d02d:00[10-49] d018:08[10-55] d028:01[26-55] d029:01[32-55] d02c:00[50-55] d011:3e[58-58] + | | | 808060 | +1 | a=3c x=08 y=06 | V_2c=Y; A=00; V_2d=A; V_18=X; X=01; V_28=X; V_29=X; X=3e; Y=08; N; N; N; N; V_2c=A; A=06; N; V_11=X +23 | 0 | 0fff | 118000 | 7 | free cc: 10/55 | d028:08[10-19] d029:06[10-25] d02c:01[10-43] d018:78[10-55] d029:08[32-55] d011:38[10-55] d02c:08[50-55] + | | | 868010 | +1 | a=06 x=3e y=08 | V_28=Y; V_29=A; A=01; V_2c=A; A=78; V_18=A; V_29=A; X=38; V_11=X; N; N; N; V_2c=A; N; N +24* | 0 | 0fff | 888080 | 7 | free cc: 9/60 | d018:68[10-55] d029:09[32-55] d02a:01[38-55] d02b:07[44-55] d02c:00[50-55] d02d:08[55-55] d011:3a[58-58] + | | | 888080 | | a=78 x=38 y=08 | A=68; V_18=A; A=3a; X=09; N; N; N; *N; V_29=X; X=01; V_2a=X; X=07; V_2b=X; *X=00; V_2c=X; V_2d=Y; V_11=A +25 | 0 | 0fff | 891708 | 8 | free cc: 2/59 | d029:01[10-25] d02a:07[10-31] d02c:09[10-43] d029:09[32-55] d018:58[10-55] d02a:08[38-55] d02c:08[50-55] d011:3c[58-58] + | | | 817798 | | a=3a x=00 y=08 | A=01; V_29=A; A=07; V_2a=A; A=09; V_2c=A; V_29=A; A=58; V_18=A; V_2a=A; X=3c; N; V_2c=A; A=01; Y=48; V_11=X +26 | 0 | 0fff | 898788 | 7 | free cc: 10/59 | d029:01[10-25] d018:48[10-55] d028:00[26-55] d029:08[32-55] d02b:00[44-55] d02c:06[50-55] d011:3e[58-58] + | | | 818788 | | a=01 x=3c y=48 | V_29=A; V_18=Y; A=00; X=06; V_28=A; N; V_29=Y; Y=3e; N; N; N; V_2b=A; A=08; V_2c=X; X=38; N; V_11=Y +27 | 0 | 0fff | 088068 | 8 | free cc: 4/55 | d02b:08[10-37] d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02b:01[44-55] d02c:08[50-55] + | | | 088868 | +1 | a=08 x=38 y=3e | V_2b=A; V_18=X; V_11=X; V_28=A; Y=01; V_29=Y; A=07; V_2a=A; A=28; V_2b=Y; Y=09; V_2c=A; N; N +28* | 0 | 0fff | 817188 | 7 | free cc: 10/60 | d018:28[10-55] d028:09[26-55] d029:08[32-55] d02a:08[38-55] d02b:07[44-55] d02d:00[55-55] d011:3a[58-58] + | | | 817188 | | a=28 x=38 y=09 | V_18=A; A=3a; Y=00; X=09; *N; V_28=X; X=08; V_29=X; N; V_2a=X; X=07; V_2b=X; X=18; N; *N; V_2d=Y; V_11=A +29 | 0 | 0fff | 988780 | 7 | free cc: 11/60 | d018:18[10-55] d028:00[26-55] d029:09[32-55] d02a:01[38-55] d02c:00[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 988780 | | a=3a x=18 y=00 | V_18=X; A=3c; X=08; N; N; V_28=Y; Y=09; V_29=Y; Y=01; V_2a=Y; Y=00; N; N; *N; V_2c=Y; V_2d=X; V_11=A +30 | 0 | 0fff | 091708 | 9 | free cc: 0/59 | d029:06[10-25] d02a:06[10-31] d018:08[10-55] d028:09[26-55] d029:08[32-55] d02a:01[38-55] d02b:00[44-55] d02c:08[50-55] d011:3e[58-58] + | | | 066708 | +1 | a=3c x=08 y=00 | A=06; V_29=A; V_2a=A; V_18=X; A=09; V_28=A; V_29=X; A=01; V_2a=A; V_2b=Y; A=3e; V_2c=X; X=78; Y=38; V_11=A +31 | 0 | 0fff | 981088 | 5 | free cc: 18/55 | d029:00[10-25] d018:78[10-55] d011:38[10-55] d029:08[32-55] d02a:07[38-55] + | | | 901088 | | a=3e x=78 y=38 | A=00; V_29=A; V_18=X; V_11=Y; A=07; N; V_29=X; X=06; V_2a=A; N; N; N; N; N; N; N; N +32* | 0 | 0fff | 987088 | 7 | free cc: 8/59 | d029:06[10-25] d018:68[10-55] d028:00[26-55] d029:01[32-55] d02a:00[38-55] d02b:08[44-55] d011:3a[58-58] + | | | 967088 | | a=07 x=06 y=38 | V_29=X; A=68; V_18=A; *X=00; V_28=X; A=01; V_29=A; A=3a; V_2a=X; X=58; V_2b=X; Y=00; N; N; N; N; V_11=A +33 | 0 | 0fff | 010888 | 5 | free cc: 22/59 | d018:58[10-55] d029:00[32-55] d02a:08[38-55] d02c:00[50-55] d011:3c[58-58] + | | | 010888 | | a=3a x=58 y=00 | V_18=X; A=3c; N; N; N; N; N; N; V_29=Y; N; V_2a=X; X=48; N; N; N; V_2c=Y; Y=01; N; V_11=A +34 | 0 | 0fff | 008808 | 7 | free cc: 11/60 | d018:48[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 008808 | | a=3c x=48 y=01 | V_18=X; A=3e; Y=00; N; N; V_28=X; X=01; V_29=X; X=07; V_2a=X; X=08; N; N; *N; V_2c=X; V_2d=Y; V_11=A +35 | 0 | 0fff | 817880 | 4 | free cc: 25/56 | d029:00[10-25] d018:38[10-55] d011:38[10-55] d02d:08[55-55] + | | | 807880 | | a=3e x=08 y=00 | V_29=Y; A=38; V_18=A; V_11=A; Y=28; N; N; N; N; N; N; N; N; N; N; N; *N; V_2d=A +36* | 0 | 0fff | 807888 | 3 | free cc: 29/59 | d018:28[10-55] d029:06[32-55] d011:3a[58-58] + | | | 807888 | | a=38 x=08 y=28 | V_18=Y; A=06; X=3a; Y=18; N; N; N; *N; V_29=A; A=00; N; N; N; N; N; N; N; N; N; N; V_11=X +37 | 0 | 0fff | 867888 | 6 | free cc: 16/59 | d02d:00[10-49] d018:18[10-55] d029:08[32-55] d02b:00[44-55] d02c:01[50-55] d011:3c[58-58] + | | | 867880 | +1 | a=00 x=3a y=18 | V_2d=A; V_18=Y; X=01; N; N; N; N; V_29=Y; Y=3c; N; N; N; V_2b=A; A=08; V_2c=X; X=3e; N; V_11=Y +38 | 0 | 0fff | 887010 | 6 | free cc: 18/59 | d02b:08[10-37] d018:08[10-55] d029:01[32-55] d02a:08[38-55] d02c:08[50-55] d011:3e[58-58] + | | | 887810 | | a=08 x=3e y=3c | V_2b=A; V_18=A; Y=01; N; N; N; N; V_29=Y; Y=07; V_2a=A; N; N; N; N; V_2c=A; A=78; N; V_11=X +39 | 0 | 0fff | 818880 | 7 | free cc: 10/55 | d02a:07[10-31] d02b:00[10-37] d018:78[10-55] d011:38[10-55] d029:00[32-55] d02a:00[38-55] d028:06[26-55] + | | | 817080 | +1 | a=78 x=3e y=07 | V_2a=Y; X=00; V_2b=X; V_18=A; A=38; V_11=A; V_29=X; V_2a=X; A=06; V_28=A; N; N; N; N; N +40* | 0 | 0fff | 600080 | 6 | free cc: 17/59 | d018:68[10-55] d028:08[26-55] d029:08[32-55] d02b:07[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 600080 | | a=06 x=00 y=07 | A=68; V_18=A; N; N; *N; V_28=A; N; V_29=A; A=3a; N; N; N; V_2b=Y; Y=58; V_2c=X; X=01; N; V_11=A +41 | 0 | 0fff | 880700 | 8 | free cc: 2/60 | d018:58[10-55] d028:00[26-55] d029:01[32-55] d02a:07[38-55] d02b:06[44-55] d02c:09[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 880700 | | a=3a x=01 y=58 | V_18=Y; A=3c; X=08; Y=00; N; V_28=Y; Y=01; V_29=Y; Y=07; V_2a=Y; Y=06; V_2b=Y; *Y=09; V_2c=Y; V_2d=X; V_11=A +42 | 0 | 0fff | 017698 | 6 | free cc: 14/59 | d02b:00[10-37] d018:48[10-55] d029:08[32-55] d02a:00[38-55] d02b:08[44-55] d011:3e[58-58] + | | | 017098 | | a=3c x=08 y=09 | A=00; V_2b=A; Y=48; V_18=Y; N; N; N; V_29=X; N; V_2a=A; A=3e; V_2b=X; X=38; Y=06; N; N; N; V_11=A +43 | 0 | 0fff | 080898 | 5 | free cc: 18/55 | d02c:08[10-43] d018:38[10-55] d011:38[10-55] d02b:06[44-55] d02c:01[50-55] + | | | 080888 | | a=3e x=38 y=06 | V_2c=X; V_18=X; V_11=X; A=01; X=28; N; N; N; N; N; N; N; V_2b=Y; Y=00; V_2c=A; N; N +44* | 0 | 0fff | 080618 | 6 | free cc: 17/59 | d02b:01[10-37] d018:28[10-55] d02a:06[38-55] d02b:00[44-55] d02c:08[50-55] d011:3a[58-58] + | | | 080118 | | a=01 x=28 y=00 | V_2b=A; V_18=X; A=06; N; N; N; N; N; N; *N; V_2a=A; A=3a; V_2b=Y; Y=18; V_2c=X; X=00; N; V_11=A +45 | 0 | 0fff | 086088 | 7 | free cc: 13/60 | d02a:00[10-31] d018:18[10-55] d029:09[32-55] d02a:06[38-55] d02c:00[50-55] d02d:09[55-55] d011:3c[58-58] + | | | 080088 | | a=3a x=00 y=18 | V_2a=X; V_18=Y; A=3c; Y=09; N; N; N; V_29=Y; X=06; V_2a=X; X=00; N; N; *N; V_2c=X; V_2d=Y; V_11=A +46 | 0 | 0fff | 096009 | 9 | free cc: 0/60 | d029:06[10-25] d02a:07[10-31] d028:09[26-55] d018:08[10-55] d029:08[32-55] d02b:07[44-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 067009 | | a=3c x=00 y=09 | A=06; V_29=A; A=07; V_2a=A; V_28=Y; Y=08; V_18=Y; V_29=Y; A=3e; Y=07; V_2b=Y; *Y=08; V_2c=Y; V_2d=X; V_11=A +47 | 0 | 0fff | 987780 | 7 | free cc: 3/56 | d02b:01[10-37] d018:78[10-55] d028:08[26-55] d011:38[10-55] d02a:00[38-55] d02b:07[44-55] d02d:09[55-55] + | | | 987180 | | a=3e x=00 y=08 | A=01; V_2b=A; A=78; V_18=A; V_28=A; A=38; V_11=A; A=07; V_2a=X; X=09; V_2b=A; A=68; Y=00; *N; V_2d=X +48* | 0 | 0fff | 880789 | 8 | free cc: 12/60 | d018:68[10-55] d028:00[26-55] d029:00[32-55] d02a:07[38-55] d02b:00[44-55] d02c:00[50-55] d02d:00[55-55] d011:3a[58-58] + | | | 880789 | | a=68 x=09 y=00 | V_18=A; A=3a; X=07; N; *N; V_28=Y; N; V_29=Y; N; V_2a=X; X=01; V_2b=Y; *N; V_2c=Y; V_2d=Y; V_11=A +49 | 0 | 0fff | 007000 | 6 | free cc: 16/59 | d02a:01[10-31] d018:58[10-55] d029:01[32-55] d02a:08[38-55] d02c:08[50-55] d011:3c[58-58] + | | | 001000 | | a=3a x=01 y=00 | V_2a=X; A=58; V_18=A; Y=3c; N; N; N; V_29=X; X=48; V_2a=A; N; N; N; N; V_2c=A; A=09; N; V_11=Y +50 | 0 | 0fff | 018080 | 5 | free cc: 18/59 | d018:48[10-55] d028:09[26-55] d029:00[32-55] d02a:07[38-55] d011:3e[58-58] + | | | 018080 | | a=09 x=48 y=3c | V_18=X; X=00; Y=07; N; N; V_28=A; A=3e; V_29=X; X=38; V_2a=Y; Y=01; N; N; N; N; N; N; N; V_11=A +51 | 0 | 0fff | 907080 | 6 | free cc: 16/55 | d018:38[10-55] d011:38[10-55] d028:08[26-55] d029:01[32-55] d02a:08[38-55] d02b:08[44-55] + | | | 907080 | | a=3e x=38 y=01 | V_18=X; V_11=X; A=28; N; V_28=A; N; V_29=Y; Y=09; V_2a=A; N; V_2b=A; N; N; N; N; N +52* | 0 | 0fff | 818880 | 6 | free cc: 15/59 | d018:28[10-55] d028:09[26-55] d029:08[32-55] d02a:00[38-55] d02c:01[50-55] d011:3a[58-58] + | | | 818880 | | a=28 x=38 y=09 | V_18=A; N; N; N; *N; V_28=Y; Y=00; V_29=A; A=01; V_2a=Y; X=3a; Y=18; N; N; V_2c=A; A=07; N; V_11=X +53 | 0 | 0fff | 980810 | 7 | free cc: 11/60 | d018:18[10-55] d028:08[26-55] d029:01[32-55] d02a:07[38-55] d02c:00[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 980810 | | a=07 x=3a y=18 | V_18=Y; A=3c; X=08; N; N; V_28=X; Y=01; V_29=Y; Y=07; V_2a=Y; Y=00; N; N; *N; V_2c=Y; V_2d=X; V_11=A +54 | 0 | 0fff | 817808 | 4 | free cc: 31/60 | d018:08[10-55] d02c:08[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 817808 | +1 | a=3c x=08 y=00 | V_18=X; A=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_2c=X; V_2d=Y; V_11=A +55 | 0 | 0fff | 817880 | 5 | free cc: 21/56 | d018:78[10-55] d011:38[10-55] d029:08[32-55] d02a:08[38-55] d02d:08[55-55] + | | | 817880 | | a=3e x=08 y=00 | A=78; V_18=A; Y=38; V_11=Y; N; N; N; V_29=A; N; V_2a=A; N; N; N; N; N; *N; V_2d=A +56* | 0 | 0fff | 888888 | 8 | free cc: 0/59 | d02d:00[10-49] d018:68[10-55] d028:09[26-55] d029:06[32-55] d02a:07[38-55] d02b:01[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 888880 | +1 | a=78 x=08 y=38 | A=00; V_2d=A; X=68; V_18=X; X=09; V_28=X; X=06; V_29=X; X=07; V_2a=X; *X=01; V_2b=X; V_2c=A; A=3a; X=58; V_11=A +57 | 0 | 0fff | 967100 | 5 | free cc: 20/59 | d018:58[10-55] d028:00[26-55] d029:00[32-55] d02c:08[50-55] d011:3c[58-58] + | | | 967100 | | a=3a x=58 y=38 | V_18=X; A=00; N; N; N; V_28=A; N; V_29=A; A=3c; N; N; N; N; N; N; V_2c=X; X=00; Y=48; V_11=A +58 | 0 | 0fff | 007180 | 7 | free cc: 10/59 | d02b:00[10-37] d018:48[10-55] d028:09[26-55] d029:01[32-55] d02a:08[38-55] d02b:08[44-55] d011:3e[58-58] + | | | 007080 | | a=3c x=00 y=48 | V_2b=X; V_18=Y; A=09; X=01; V_28=A; A=3e; V_29=X; X=38; V_2a=X; N; V_2b=X; Y=00; N; N; N; N; V_11=A +59 | 0 | 0fff | 918880 | 6 | free cc: 12/55 | d018:38[10-55] d011:38[10-55] d028:00[26-55] d029:00[32-55] d02a:07[38-55] d02b:01[44-55] + | | | 918880 | | a=3e x=38 y=00 | V_18=X; V_11=X; A=07; X=01; V_28=Y; N; V_29=Y; Y=28; V_2a=A; A=00; V_2b=X; N; N; N; N; N +60* | 0 | 0fff | 007180 | 5 | free cc: 23/59 | d018:28[10-55] d028:08[26-55] d029:08[32-55] d02b:00[44-55] d011:3a[58-58] + | | | 007180 | | a=00 x=01 y=28 | V_18=Y; X=3a; N; N; *N; V_28=Y; N; V_29=Y; Y=18; N; N; N; V_2b=A; A=09; N; N; N; N; V_11=X +61 | 0 | 0fff | 887080 | 10 | free cc: 0/60 | d02b:08[10-37] d018:18[10-55] d001:d4[10-55] d003:d4[10-55] d005:d4[10-55] d007:d4[10-55] d009:d4[10-55] d00b:d4[10-55] d02d:09[55-55] d011:3c[58-58] + | | | 887880 | +6 | a=09 x=3a y=18 | V_2b=Y; V_18=Y; X=d4; V_01=X; V_03=X; V_05=X; V_07=X; V_09=X; V_0b=X; A=3c; X=09; *Y=08; V_2d=X; V_11=A +62 | 0 | 0fff | 887889 | 9 | free cc: 6/60 | d018:08[10-55] d00d:d4[10-55] d00f:d4[10-55] d028:09[26-55] d02a:08[38-55] d02b:09[44-55] d02c:00[50-55] d02d:08[55-55] d011:3e[58-58] + | | | 887889 | +2 | a=3c x=09 y=08 | V_18=Y; A=d4; V_0d=A; V_0f=A; V_28=X; A=3e; N; N; V_2a=Y; N; V_2b=X; *X=00; V_2c=X; V_2d=Y; V_11=A +63 | 0 | 0fff | 988908 | 7 | free cc: 8/56 | d02b:00[10-37] dd00:ffffff03[10-55] d011:38[10-55] d028:08[26-55] d02b:07[44-55] d02c:08[50-55] d02d:09[55-55] + | | | 988008 | | a=3e x=00 y=08 | V_2b=X; A=ffffff03; V_00=A; A=38; V_11=A; V_28=A; X=07; N; N; N; N; V_2b=X; *X=09; V_2c=A; V_2d=X +64* | 0 | 0fff | 888789 | 8 | free cc: 4/59 | d02d:00[10-49] d018:98[10-55] d028:09[26-55] d029:09[32-55] d02a:01[38-55] d02b:01[44-55] d02c:00[50-55] d011:3a[58-58] + | | | 888780 | +1 | a=38 x=09 y=08 | A=00; V_2d=A; *Y=98; V_18=Y; V_28=X; Y=01; V_29=X; X=3a; V_2a=Y; N; V_2b=Y; Y=08; V_2c=A; A=a8; N; V_11=X +65 | 0 | 0fff | 991100 | 8 | free cc: 6/59 | d02b:08[10-37] d02c:08[10-43] d018:a8[10-55] d029:08[32-55] d02a:00[38-55] d02b:00[44-55] d02c:06[50-55] d011:3c[58-58] + | | | 991880 | | a=a8 x=3a y=08 | V_2b=A; V_2c=A; V_18=A; X=00; N; N; V_29=A; A=06; V_2a=X; Y=3c; V_2b=X; X=b8; V_2c=A; A=09; N; V_11=Y +66 | 0 | 0fff | 980060 | 6 | free cc: 16/59 | d018:b8[10-55] d029:09[32-55] d02a:08[38-55] d02b:09[44-55] d02c:01[50-55] d011:3e[58-58] + | | | 980060 | +1 | a=09 x=b8 y=3c | V_18=X; Y=01; N; N; N; N; N; N; V_29=A; N; V_2a=X; X=3e; V_2b=A; A=08; V_2c=Y; Y=88; N; V_11=X +67 | 0 | 0fff | 998910 | 8 | free cc: 3/56 | d028:08[10-19] d02b:07[10-37] d018:88[10-55] d011:38[10-55] d028:00[26-55] d02a:00[38-55] d02c:00[50-55] d02d:06[55-55] + | | | 898710 | +2 | a=08 x=3e y=88 | V_28=A; A=07; V_2b=A; V_18=Y; A=38; V_11=A; A=00; V_28=A; V_2a=A; X=06; Y=07; *N; V_2c=A; V_2d=X +68* | 0 | 0fff | 090706 | 6 | free cc: 15/59 | d028:08[10-19] d02a:07[10-31] d02c:09[10-43] d018:98[10-55] d028:09[26-55] d011:3a[58-58] + | | | 897796 | | a=00 x=06 y=07 | A=98; V_28=A; V_2a=Y; X=09; V_2c=X; V_18=A; V_28=X; A=3a; X=00; Y=a8; N; N; N; N; N; N; *N; V_11=A +69 | 0 | 0fff | 997796 | 8 | free cc: 6/59 | d028:00[10-19] d02a:00[10-31] d02c:00[10-43] d018:a8[10-55] d028:09[26-55] d02a:08[38-55] d02c:06[50-55] d011:3c[58-58] + | | | 090706 | | a=3a x=00 y=a8 | V_28=X; V_2a=X; V_2c=X; V_18=Y; A=09; V_28=A; A=06; V_2a=Y; X=3c; Y=07; N; N; V_2c=A; A=00; N; V_11=X +70 | 0 | 0fff | 998766 | 7 | free cc: 10/59 | d02a:07[10-31] d02c:00[10-43] d02d:00[10-49] d018:b8[10-55] d02a:00[38-55] d02c:09[50-55] d011:3e[58-58] + | | | 997700 | | a=00 x=3c y=07 | V_2a=Y; V_2c=A; V_2d=A; X=b8; V_18=X; X=09; Y=3e; N; V_2a=A; A=08; N; N; N; V_2c=X; X=06; N; V_11=Y +71 | 0 | 0fff | 990790 | 8 | free cc: 3/56 | d02c:08[10-43] d02d:06[10-49] d018:88[10-55] d011:38[10-55] d028:01[26-55] d02a:07[38-55] d02c:06[50-55] d02d:01[55-55] + | | | 990786 | | a=08 x=06 y=3e | V_2c=A; V_2d=X; A=88; V_18=A; A=38; V_11=A; A=01; V_28=A; Y=07; V_2a=Y; Y=08; *N; V_2c=X; V_2d=A +72* | 0 | 0fff | 197761 | 6 | free cc: 13/59 | d028:08[10-19] d02c:00[10-43] d018:98[10-55] d02a:00[38-55] d02c:09[50-55] d011:3a[58-58] + | | | 897701 | | a=01 x=06 y=08 | V_28=Y; A=00; V_2c=A; X=98; V_18=X; X=09; Y=3a; N; *N; V_2a=A; A=a8; N; N; N; V_2c=X; X=3c; N; V_11=Y +73 | 0 | 0fff | 890791 | 7 | free cc: 15/60 | d02d:09[10-49] d018:a8[10-55] d029:08[32-55] d02a:08[38-55] d02c:08[50-55] d02d:08[55-55] d011:3c[58-58] + | | | 890799 | | a=a8 x=3c y=3a | Y=09; V_2d=Y; V_18=A; A=3c; Y=08; N; N; V_29=Y; N; V_2a=Y; N; N; N; *N; V_2c=Y; V_2d=Y; V_11=A +74 | 0 | 0fff | 888788 | 6 | free cc: 17/60 | d018:b8[10-55] d028:00[26-55] d029:09[32-55] d02c:09[50-55] d02d:00[55-55] d011:3e[58-58] + | | | 888788 | | a=3c x=3c y=08 | A=b8; V_18=A; A=3e; X=00; Y=09; V_28=X; N; V_29=Y; N; N; N; N; N; N; *N; V_2c=Y; V_2d=X; V_11=A +75 | 0 | 0fff | 098790 | 7 | free cc: 10/56 | d018:88[10-55] d011:38[10-55] d028:08[26-55] d029:00[32-55] d02b:00[44-55] d02c:08[50-55] d02d:08[55-55] + | | | 098790 | | a=3e x=00 y=09 | A=88; V_18=A; Y=38; V_11=Y; V_28=A; N; V_29=X; N; N; N; N; V_2b=X; *X=98; V_2c=A; V_2d=A +76* | 0 | 0fff | 808088 | 6 | free cc: 15/60 | d018:98[10-55] d02a:07[38-55] d02b:08[44-55] d02c:00[50-55] d02d:00[55-55] d011:3a[58-58] + | | | 808088 | | a=88 x=98 y=38 | V_18=X; A=3a; X=00; Y=07; N; N; N; N; N; N; *N; V_2a=Y; Y=08; V_2b=Y; *Y=09; V_2c=X; V_2d=X; V_11=A +77 | 0 | 0fff | 807800 | 6 | free cc: 15/60 | d029:09[10-25] d018:a8[10-55] d029:00[32-55] d02b:01[44-55] d02d:08[55-55] d011:3c[58-58] + | | | 897800 | +1 | a=3a x=00 y=09 | V_29=Y; A=a8; V_18=A; A=3c; Y=08; N; N; V_29=X; X=01; N; N; N; V_2b=X; X=b8; N; *N; V_2d=X; V_11=A +78 | 0 | 0fff | 807108 | 7 | free cc: 6/59 | d018:b8[10-55] d028:00[26-55] d029:03[32-55] d02a:00[38-55] d02b:08[44-55] d02c:09[50-55] d011:3e[58-58] + | | | 807108 | | a=3c x=b8 y=08 | V_18=X; A=00; N; N; N; V_28=A; A=03; V_29=A; A=00; V_2a=A; A=09; V_2b=X; X=3e; V_2c=A; A=00; Y=88; V_11=X +79 | 0 | 0fff | 030898 | 6 | free cc: 16/55 | d028:08[10-19] d029:00[10-25] d02c:00[10-43] d018:88[10-55] d011:38[10-55] d02a:07[38-55] + | | | 800808 | +2 | a=00 x=3e y=88 | V_28=Y; V_29=A; V_2c=A; V_18=Y; A=38; V_11=A; A=07; V_2a=A; N; N; N; N; N; N; N; N +80* | 0 | 0fff | 807808 | 6 | free cc: 13/59 | d029:08[10-25] d02a:01[10-31] d018:98[10-55] d02a:08[38-55] d02b:00[44-55] d011:3a[58-58] + | | | 881808 | | a=07 x=3e y=88 | V_29=Y; A=01; V_2a=A; A=98; V_18=A; X=00; N; N; *N; V_2a=A; A=3a; V_2b=X; X=03; Y=06; N; N; N; V_11=A +81 | 0 | 0fff | 888008 | 9 | free cc: 0/59 | d028:03[10-19] d029:03[10-25] d02a:06[10-31] d02b:03[10-37] d02c:03[10-43] d018:a8[10-55] d028:09[26-55] d02a:00[38-55] d011:3c[58-58] + | | | 336338 | +1 | a=3a x=03 y=06 | V_28=X; V_29=X; V_2a=Y; V_2b=X; V_2c=X; A=a8; V_18=A; A=09; V_28=A; A=00; V_2a=A; A=3c; X=b8; Y=03; V_11=A +82 | 0 | 0fff | 930338 | 5 | free cc: 22/59 | d018:b8[10-55] d029:08[32-55] d02a:03[38-55] d02c:08[50-55] d011:3e[58-58] + | | | 930338 | | a=3c x=b8 y=03 | V_18=X; A=3e; N; N; N; N; N; N; V_29=X; N; V_2a=Y; Y=98; N; N; N; V_2c=X; X=38; N; V_11=A +83 | 0 | 0fff | 983388 | 3 | free cc: 26/55 | d018:98[10-55] d011:38[10-55] d02c:03[50-55] + | | | 983388 | | a=3e x=38 y=98 | V_18=Y; V_11=X; A=03; X=09; Y=a8; N; N; N; N; N; N; N; N; N; N; N; V_2c=A; N; N +84* | 0 | 0fff | 983338 | 8 | free cc: 8/59 | d02d:09[10-49] d018:a8[10-55] d028:01[26-55] d029:01[32-55] d02a:01[38-55] d02b:01[44-55] d02c:01[50-55] d011:3a[58-58] + | | | 983339 | | a=03 x=09 y=a8 | V_2d=X; V_18=Y; A=01; *X=3a; V_28=A; Y=09; V_29=A; N; V_2a=A; N; V_2b=A; N; V_2c=A; A=06; N; V_11=X +85 | 0 | 0fff | 111119 | 10 | free cc: 0/60 | d028:08[10-19] d029:00[10-25] d02a:00[10-31] d02b:08[10-37] d02c:09[10-43] d018:b8[10-55] d02a:07[38-55] d02b:00[44-55] d02d:00[55-55] d011:3c[58-58] + | | | 800899 | | a=06 x=3a y=09 | A=b8; V_28=A; X=00; V_29=X; V_2a=X; V_2b=A; V_2c=Y; V_18=A; A=07; V_2a=A; V_2b=X; *A=3c; V_2d=X; V_11=A +86 | 0 | 0fff | 807090 | 7 | free cc: 10/59 | d029:07[10-25] d018:88[10-55] d028:00[26-55] d029:08[32-55] d02a:00[38-55] d02c:00[50-55] d011:3e[58-58] + | | | 877090 | | a=3c x=00 y=09 | A=07; V_29=A; A=88; V_18=A; V_28=X; Y=3e; V_29=A; A=07; V_2a=X; N; N; N; N; V_2c=X; X=98; N; V_11=Y +87 | 0 | 0fff | 080000 | 5 | free cc: 20/55 | d02a:07[10-31] d018:98[10-55] d011:38[10-55] d028:08[26-55] d029:00[32-55] + | | | 087000 | | a=07 x=98 y=3e | V_2a=A; V_18=X; A=38; V_11=A; V_28=A; A=00; V_29=A; N; N; N; N; N; N; N; N; N; N +88* | 0 | 0fff | 807000 | 6 | free cc: 16/60 | d018:a8[10-55] d029:07[32-55] d02a:08[38-55] d02b:07[44-55] d02d:08[55-55] d011:3a[58-58] + | | | 807000 | | a=00 x=98 y=3e | A=a8; V_18=A; A=3a; Y=08; X=07; N; N; *N; V_29=X; N; V_2a=Y; N; V_2b=X; X=00; N; *N; V_2d=Y; V_11=A +89 | 0 | 0fff | 878708 | 5 | free cc: 20/59 | d02d:00[10-49] d018:b8[10-55] d028:00[26-55] d02a:00[38-55] d011:3c[58-58] + | | | 878700 | | a=3a x=00 y=08 | V_2d=X; A=b8; V_18=A; A=3c; V_28=X; Y=09; N; N; N; V_2a=X; X=88; N; N; N; N; N; N; N; V_11=A +90 | 0 | 0fff | 070700 | 5 | free cc: 25/60 | d029:09[10-25] d018:88[10-55] d029:08[32-55] d02d:08[55-55] d011:3e[58-58] + | | | 090700 | | a=3c x=88 y=09 | V_29=Y; V_18=X; A=3e; Y=08; N; N; N; V_29=X; N; N; N; N; N; N; N; N; *N; V_2d=X; V_11=A +91 | 0 | 0fff | 080708 | 5 | free cc: 17/56 | d018:98[10-55] d011:38[10-55] d029:07[32-55] d02c:08[50-55] d02d:00[55-55] + | | | 080708 | | a=3e x=88 y=08 | A=98; V_18=A; A=38; V_11=A; A=07; N; N; V_29=A; A=00; N; N; N; N; N; *N; V_2c=X; V_2d=A +92* | 0 | 0fff | 070780 | 6 | free cc: 14/60 | d029:09[10-25] d02b:01[10-37] d018:a8[10-55] d02b:00[44-55] d02d:08[55-55] d011:3a[58-58] + | | | 090180 | | a=00 x=88 y=08 | A=09; V_29=A; A=01; V_2b=A; A=a8; V_18=A; A=3a; X=00; N; N; N; *N; V_2b=X; X=07; N; *N; V_2d=Y; V_11=A +93 | 0 | 0fff | 090088 | 5 | free cc: 18/59 | d029:07[10-25] d018:b8[10-55] d029:00[32-55] d02a:07[38-55] d011:3c[58-58] + | | | 070088 | | a=3a x=07 y=08 | V_29=X; A=b8; V_18=A; A=00; Y=3c; N; N; V_29=A; A=88; V_2a=X; X=00; N; N; N; N; N; N; N; V_11=Y +94 | 0 | 0fff | 007088 | 5 | free cc: 20/59 | d018:88[10-55] d029:08[32-55] d02a:00[38-55] d02b:07[44-55] d011:3e[58-58] + | | | 007088 | | a=88 x=00 y=3c | V_18=A; Y=07; N; N; N; N; N; N; V_29=A; A=3e; V_2a=X; X=98; V_2b=Y; Y=38; N; N; N; N; V_11=A +95 | 0 | 0fff | 080788 | 5 | free cc: 16/55 | d018:98[10-55] d011:38[10-55] d029:00[32-55] d02a:07[38-55] d02c:00[50-55] + | | | 080788 | | a=3e x=98 y=38 | V_18=X; V_11=Y; A=00; X=07; Y=a8; N; N; V_29=A; N; V_2a=X; X=3a; N; N; N; V_2c=A; N; N +96* | 0 | 0fff | 007708 | 2 | free cc: 37/59 | d018:a8[10-55] d011:3a[58-58] + | | | 007708 | | a=00 x=3a y=a8 | V_18=Y; A=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=X +97 | 0 | 0fff | 007708 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 007708 | | a=b8 x=3a y=3c | V_18=A; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +98 | 0 | 0fff | 007708 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 007708 | | a=88 x=3e y=3c | V_18=A; A=18; Y=10; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +99 | 0 | 0fff | 007708 | 2 | free cc: 38/57 | d018:18[10-55] d011:10[56-56] + | | | | | a=18 x=3e y=10 | V_18=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +free cycles = 1461 + +Resolutions: +Included 2 UnderlayColor d02c:00/32-37/6-7 +Included 2 UnderlayColor d02a:09/26-37/6-9 +Included 12 UnderlayColor d02d:09/0a-31/25-27 +Included 14 UnderlayColor d028:09/1a-37/30-36 +Included 15 UnderlayColor d029:03/20-37/32-37 +Included 17 UnderlayColor d02a:00/0a-1f/35-36 +Included 17 UnderlayColor d02b:00/0a-25/35-37 +Included 22 UnderlayColor d02d:00/0a-31/45-48 +Included 23 UnderlayColor d02c:08/32-37/48-49 +Included 27 UnderlayColor d029:01/20-37/56-57 +Included 30 UnderlayColor d02b:00/2c-37/62-64 +Included 37 UnderlayColor d02d:00/0a-31/75-82 +Included 39 UnderlayColor d02b:00/0a-25/79-80 +Included 54 UnderlayColor d02d:00/37-37/110-111 +Included 56 UnderlayColor d02d:00/0a-31/113-114 +Included 61 SpriteY d001:d4/0a-37/123-149 +Included 61 SpriteY d003:d4/0a-37/123-151 +Included 61 SpriteY d005:d4/0a-37/123-153 +Included 61 SpriteY d007:d4/0a-37/123-155 +Included 61 SpriteY d009:d4/0a-37/123-157 +Included 61 SpriteY d00b:d4/0a-37/123-159 +Included 62 SpriteY d00d:d4/0a-37/123-161 +Included 62 SpriteY d00f:d4/0a-37/123-163 +Included 64 UnderlayColor d02d:00/0a-31/129-135 +Included 66 UnderlayColor d029:09/20-37/134-147 +Included 67 UnderlayColor d02b:07/0a-25/135-137 +Included 67 UnderlayColor d02d:06/37-37/136-140 +Included 77 UnderlayColor d02d:08/37-37/156-166 +Included 79 UnderlayColor d02c:00/0a-2b/159-160 +Included 79 UnderlayColor d028:08/0a-13/159-162 +Included 81 UnderlayColor d029:03/0a-19/163-164 +Overflow 85 (67): Overriding Underlay 85 6 d028:09/0a-13/171-171 -> 08 +Overflow 85 (63): Overriding Underlay 85 7 d029:06/0a-19/171-171 -> 00 diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.mui b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.mui new file mode 100644 index 0000000..2a43589 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.mui differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nfxprof b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nfxprof new file mode 100644 index 0000000..0d40290 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nfxprof @@ -0,0 +1,94 @@ +{ + "Mode": 1, + "Brightness": -28, + "Contrast": 0, + "Saturation": 0, + "SourceColors": [ + { + "r": 0, + "g": 0, + "b": 0, + "a": 255 + }, + { + "r": 0, + "g": 66, + "b": 139, + "a": 255 + }, + { + "r": 0, + "g": 214, + "b": 254, + "a": 255 + }, + { + "r": 106, + "g": 107, + "b": 0, + "a": 255 + }, + { + "r": 214, + "g": 181, + "b": 0, + "a": 255 + }, + { + "r": 182, + "g": 140, + "b": 0, + "a": 255 + }, + { + "r": 140, + "g": 106, + "b": 0, + "a": 255 + }, + { + "r": 0, + "g": 254, + "b": 255, + "a": 255 + }, + { + "r": 180, + "g": 255, + "b": 253, + "a": 255 + }, + { + "r": 255, + "g": 255, + "b": 107, + "a": 255 + }, + { + "r": 255, + "g": 214, + "b": 0, + "a": 255 + }, + { + "r": 255, + "g": 255, + "b": 255, + "a": 255 + } + ], + "TargetIndices": [ + 0, + 6, + 3, + 9, + 8, + 8, + 9, + 3, + 3, + 1, + 7, + 1 + ] +} \ No newline at end of file diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nuf b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nuf new file mode 100644 index 0000000..b25b9ee Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.nuf differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.png new file mode 100644 index 0000000..fff7b7e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.prg b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.prg new file mode 100644 index 0000000..d029e3b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_320.prg differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_clashstate.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_clashstate.png new file mode 100644 index 0000000..9d0a769 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_clashstate.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.bmp b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.bmp new file mode 100644 index 0000000..2fd7620 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.png new file mode 100644 index 0000000..7b305a5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1.png new file mode 100644 index 0000000..5b962ec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.bmp b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.bmp new file mode 100644 index 0000000..9e0417e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.png new file mode 100644 index 0000000..a16db8e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i1_2x.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2.png new file mode 100644 index 0000000..2227922 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.bmp b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.bmp new file mode 100644 index 0000000..c5500f6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.png new file mode 100644 index 0000000..258ba7a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_i2_2x.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_nooverscan.png b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_nooverscan.png new file mode 100644 index 0000000..ff9402c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/02_title/title_ifli_nooverscan.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/01.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/01.bmp new file mode 100644 index 0000000..910862d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/02.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/02.bmp new file mode 100644 index 0000000..d89fcda Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/03.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/03.bmp new file mode 100644 index 0000000..0d27ad9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/04.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/04.bmp new file mode 100644 index 0000000..b8f0c83 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/05.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/05.bmp new file mode 100644 index 0000000..2c3472e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/06.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/06.bmp new file mode 100644 index 0000000..e38585c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/07.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/07.bmp new file mode 100644 index 0000000..d764d7e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/08.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/08.bmp new file mode 100644 index 0000000..ab9b2c6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/09.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/09.bmp new file mode 100644 index 0000000..77e4bd2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/10.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/10.bmp new file mode 100644 index 0000000..5595e55 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/11.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/11.bmp new file mode 100644 index 0000000..418970b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/12.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/12.bmp new file mode 100644 index 0000000..6e742c2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/13.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/13.bmp new file mode 100644 index 0000000..4d8464b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/14.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/14.bmp new file mode 100644 index 0000000..489b4f9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/15.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/15.bmp new file mode 100644 index 0000000..e5efed4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/01.bmp new file mode 100644 index 0000000..447d7e1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/02.bmp new file mode 100644 index 0000000..78226e0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/03.bmp new file mode 100644 index 0000000..ad55e85 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/04.bmp new file mode 100644 index 0000000..e9260b9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/05.bmp new file mode 100644 index 0000000..a578207 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/06.bmp new file mode 100644 index 0000000..fc6e91a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/07.bmp new file mode 100644 index 0000000..ad8553d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/08.bmp new file mode 100644 index 0000000..63011f1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/09.bmp new file mode 100644 index 0000000..10779c4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/10.bmp new file mode 100644 index 0000000..f9a36a8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/11.bmp new file mode 100644 index 0000000..dff5af9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12 - Copy.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12 - Copy.bmp new file mode 100644 index 0000000..0ad3ab7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12 - Copy.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12.bmp new file mode 100644 index 0000000..6e742c2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/13.bmp new file mode 100644 index 0000000..4ae4d2d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/14.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/14.bmp new file mode 100644 index 0000000..6f7d272 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/15.bmp b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/15.bmp new file mode 100644 index 0000000..2376ac2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/01.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/01.png new file mode 100644 index 0000000..5ffe1d6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/02.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/02.png new file mode 100644 index 0000000..3b1fcf4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03.png new file mode 100644 index 0000000..5a07e0b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03_tower.zip b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03_tower.zip new file mode 100644 index 0000000..69edb9c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/03_tower.zip differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/04.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/04.png new file mode 100644 index 0000000..2b30763 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/05.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/05.png new file mode 100644 index 0000000..ca5f51c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/06.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/06.png new file mode 100644 index 0000000..fa8d111 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/07.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/07.png new file mode 100644 index 0000000..4c5aa1c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/08.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/08.png new file mode 100644 index 0000000..61eaa35 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/09.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/09.png new file mode 100644 index 0000000..aa261dc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/10.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/10.png new file mode 100644 index 0000000..37b9d5c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/11.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/11.png new file mode 100644 index 0000000..4c8a87f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/12.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/12.png new file mode 100644 index 0000000..b994446 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/13.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/13.png new file mode 100644 index 0000000..f8d9755 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/14.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/14.png new file mode 100644 index 0000000..e230c1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/15.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/15.png new file mode 100644 index 0000000..4c136a9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/cropped/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/01.png new file mode 100644 index 0000000..d09cf1a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/02.png new file mode 100644 index 0000000..04f03b6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/03.png new file mode 100644 index 0000000..0a1ca49 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/04.png new file mode 100644 index 0000000..4639624 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/05.png new file mode 100644 index 0000000..160568f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/06.png new file mode 100644 index 0000000..f9dd91e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/07.png new file mode 100644 index 0000000..339137a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/08.png new file mode 100644 index 0000000..c7908f5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/09.png new file mode 100644 index 0000000..1b2543c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/10.png new file mode 100644 index 0000000..d09f91c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/11.png new file mode 100644 index 0000000..c081c1a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/12.png new file mode 100644 index 0000000..b3706bf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/13.png new file mode 100644 index 0000000..4f77663 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/14.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/14.png new file mode 100644 index 0000000..bec2e66 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/15.png b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/15.png new file mode 100644 index 0000000..323ddad Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/03_tower/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/01.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/01.bmp new file mode 100644 index 0000000..bd903d1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/02.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/02.bmp new file mode 100644 index 0000000..576de35 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/03.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/03.bmp new file mode 100644 index 0000000..5b9c0cb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/04.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/04.bmp new file mode 100644 index 0000000..abb6e44 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/05.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/05.bmp new file mode 100644 index 0000000..26fc46c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/06.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/06.bmp new file mode 100644 index 0000000..65cabe0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/07.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/07.bmp new file mode 100644 index 0000000..75213b5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/08.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/08.bmp new file mode 100644 index 0000000..9ca39f2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/09.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/09.bmp new file mode 100644 index 0000000..74e3f06 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/10.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/10.bmp new file mode 100644 index 0000000..a1fce0d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/11.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/11.bmp new file mode 100644 index 0000000..f175bca Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/12.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/12.bmp new file mode 100644 index 0000000..b014ac8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/13.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/13.bmp new file mode 100644 index 0000000..c62226b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/01.bmp new file mode 100644 index 0000000..2eaccf7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/02.bmp new file mode 100644 index 0000000..2be5df2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/03.bmp new file mode 100644 index 0000000..f018218 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/04.bmp new file mode 100644 index 0000000..34fb211 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/05.bmp new file mode 100644 index 0000000..cba40de Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/06.bmp new file mode 100644 index 0000000..4eaa1d3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/07.bmp new file mode 100644 index 0000000..8ee9ea7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/08.bmp new file mode 100644 index 0000000..c673e80 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/09.bmp new file mode 100644 index 0000000..2aae831 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/10.bmp new file mode 100644 index 0000000..fb803c5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/11.bmp new file mode 100644 index 0000000..15ddc61 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/12.bmp new file mode 100644 index 0000000..0c2034d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/13.bmp new file mode 100644 index 0000000..15e3ca3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/01.png new file mode 100644 index 0000000..0eeb54d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/02.png new file mode 100644 index 0000000..e6e9230 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/03.png new file mode 100644 index 0000000..74c6215 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/04.png new file mode 100644 index 0000000..263842f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/05.png new file mode 100644 index 0000000..cb206e0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/06.png new file mode 100644 index 0000000..894b1b6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/07.png new file mode 100644 index 0000000..7da05fc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/08.png new file mode 100644 index 0000000..04ba9ea Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/09.png new file mode 100644 index 0000000..233cb87 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/10.png new file mode 100644 index 0000000..f380ca5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/11.png new file mode 100644 index 0000000..5a7d240 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/12.png new file mode 100644 index 0000000..d4fac1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/13.png new file mode 100644 index 0000000..59fc55a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/04_tower_beam/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/01.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/01.bmp new file mode 100644 index 0000000..049d50f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/02.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/02.bmp new file mode 100644 index 0000000..b77b8ef Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/03.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/03.bmp new file mode 100644 index 0000000..3c05604 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/04.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/04.bmp new file mode 100644 index 0000000..f666f07 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/05.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/05.bmp new file mode 100644 index 0000000..d5bd4c1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/06.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/06.bmp new file mode 100644 index 0000000..a608b27 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/07.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/07.bmp new file mode 100644 index 0000000..64fc1cb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/08.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/08.bmp new file mode 100644 index 0000000..9bb93ef Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/09.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/09.bmp new file mode 100644 index 0000000..7b95470 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/10.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/10.bmp new file mode 100644 index 0000000..63c675a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/11.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/11.bmp new file mode 100644 index 0000000..27effb5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/12.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/12.bmp new file mode 100644 index 0000000..35d839c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/13.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/13.bmp new file mode 100644 index 0000000..52b4552 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/14.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/14.bmp new file mode 100644 index 0000000..a5d82dc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/15.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/15.bmp new file mode 100644 index 0000000..8818dd3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/16.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/16.bmp new file mode 100644 index 0000000..3e76fd2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/17.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/17.bmp new file mode 100644 index 0000000..ac85d97 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/18.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/18.bmp new file mode 100644 index 0000000..b960e4d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/19.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/19.bmp new file mode 100644 index 0000000..fc9d949 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/20.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/20.bmp new file mode 100644 index 0000000..de70720 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/20.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/21.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/21.bmp new file mode 100644 index 0000000..b800127 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/21.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/22.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/22.bmp new file mode 100644 index 0000000..c18eb6d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/22.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/23.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/23.bmp new file mode 100644 index 0000000..5dd70be Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/23.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/24.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/24.bmp new file mode 100644 index 0000000..59654e4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/24.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/25.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/25.bmp new file mode 100644 index 0000000..2dd73f6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/25.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/26.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/26.bmp new file mode 100644 index 0000000..91382bd Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/26.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/27.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/27.bmp new file mode 100644 index 0000000..93d134f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/27.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/28.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/28.bmp new file mode 100644 index 0000000..4e23881 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/28.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/29.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/29.bmp new file mode 100644 index 0000000..bf18642 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/29.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/30.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/30.bmp new file mode 100644 index 0000000..3f96d1e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/30.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/01.bmp new file mode 100644 index 0000000..56dd0e5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/02.bmp new file mode 100644 index 0000000..e30e644 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/03.bmp new file mode 100644 index 0000000..70da67b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/04.bmp new file mode 100644 index 0000000..bb59216 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/05.bmp new file mode 100644 index 0000000..4bafefb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/06.bmp new file mode 100644 index 0000000..6dca3f1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/07.bmp new file mode 100644 index 0000000..739c891 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/08.bmp new file mode 100644 index 0000000..566467f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/09.bmp new file mode 100644 index 0000000..22b34a9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/10.bmp new file mode 100644 index 0000000..b7ae6b2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/11.bmp new file mode 100644 index 0000000..4fe700c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/12.bmp new file mode 100644 index 0000000..3de9b6f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/13.bmp new file mode 100644 index 0000000..59122ff Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/14.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/14.bmp new file mode 100644 index 0000000..0002788 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/15.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/15.bmp new file mode 100644 index 0000000..741a63f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/16.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/16.bmp new file mode 100644 index 0000000..bb9e5d0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/17.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/17.bmp new file mode 100644 index 0000000..49b6839 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/18.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/18.bmp new file mode 100644 index 0000000..5057e17 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/19.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/19.bmp new file mode 100644 index 0000000..4505d1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/20.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/20.bmp new file mode 100644 index 0000000..c86237d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/20.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/21.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/21.bmp new file mode 100644 index 0000000..a589521 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/21.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/22.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/22.bmp new file mode 100644 index 0000000..1eade56 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/22.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/23.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/23.bmp new file mode 100644 index 0000000..5d722f3 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/23.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/24.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/24.bmp new file mode 100644 index 0000000..17caa52 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/24.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/25.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/25.bmp new file mode 100644 index 0000000..2000a55 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/25.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/26.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/26.bmp new file mode 100644 index 0000000..a8180ed Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/26.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/27.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/27.bmp new file mode 100644 index 0000000..030373f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/27.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/28.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/28.bmp new file mode 100644 index 0000000..a252b31 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/28.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/29.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/29.bmp new file mode 100644 index 0000000..39ca8d2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/29.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/30.bmp b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/30.bmp new file mode 100644 index 0000000..dc6310d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/30.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/01.png new file mode 100644 index 0000000..bc146c1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/02.png new file mode 100644 index 0000000..d64438c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/03.png new file mode 100644 index 0000000..985445c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/04.png new file mode 100644 index 0000000..5168bbf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/05.png new file mode 100644 index 0000000..2bf4bcb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/06.png new file mode 100644 index 0000000..451353a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/07.png new file mode 100644 index 0000000..fe27068 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/08.png new file mode 100644 index 0000000..c118e19 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/09.png new file mode 100644 index 0000000..b2066e0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/10.png new file mode 100644 index 0000000..ce5f22d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/11.png new file mode 100644 index 0000000..d388a15 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/12.png new file mode 100644 index 0000000..462150e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/13.png new file mode 100644 index 0000000..65f076f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/14.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/14.png new file mode 100644 index 0000000..0e7f4d9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/15.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/15.png new file mode 100644 index 0000000..a4ca64d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/16.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/16.png new file mode 100644 index 0000000..e88f221 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/16.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/17.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/17.png new file mode 100644 index 0000000..8d792fb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/17.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/18.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/18.png new file mode 100644 index 0000000..ee05aef Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/18.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/19.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/19.png new file mode 100644 index 0000000..b9a54f6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/19.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/20.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/20.png new file mode 100644 index 0000000..37f9ecf Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/20.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/21.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/21.png new file mode 100644 index 0000000..c176adc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/21.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/22.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/22.png new file mode 100644 index 0000000..a458fe4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/22.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/23.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/23.png new file mode 100644 index 0000000..c56029c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/23.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/24.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/24.png new file mode 100644 index 0000000..8c44fab Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/24.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/25.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/25.png new file mode 100644 index 0000000..7534147 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/25.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/26.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/26.png new file mode 100644 index 0000000..4f49d8e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/26.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/27.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/27.png new file mode 100644 index 0000000..1951c0a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/27.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/28.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/28.png new file mode 100644 index 0000000..bb53da5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/28.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/29.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/29.png new file mode 100644 index 0000000..8ece8a6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/29.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/30.png b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/30.png new file mode 100644 index 0000000..4933647 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/05_field/koala/png/30.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/01.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/01.bmp new file mode 100644 index 0000000..4429d28 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/02.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/02.bmp new file mode 100644 index 0000000..a649c0b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/03.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/03.bmp new file mode 100644 index 0000000..b36f84d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/04.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/04.bmp new file mode 100644 index 0000000..9243cea Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/05.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/05.bmp new file mode 100644 index 0000000..6bf9601 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/06.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/06.bmp new file mode 100644 index 0000000..6633215 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/07.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/07.bmp new file mode 100644 index 0000000..2d1e9f9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/08.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/08.bmp new file mode 100644 index 0000000..7d68d6f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/09.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/09.bmp new file mode 100644 index 0000000..2caaf87 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/10.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/10.bmp new file mode 100644 index 0000000..8f1f08b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/11.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/11.bmp new file mode 100644 index 0000000..9c48e59 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/12.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/12.bmp new file mode 100644 index 0000000..dc48b1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/13.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/13.bmp new file mode 100644 index 0000000..919e9aa Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/14.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/14.bmp new file mode 100644 index 0000000..722131d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/15.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/15.bmp new file mode 100644 index 0000000..f03657e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/16.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/16.bmp new file mode 100644 index 0000000..cf4aaec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/17.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/17.bmp new file mode 100644 index 0000000..4e11d08 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/18.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/18.bmp new file mode 100644 index 0000000..a768632 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/19.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/19.bmp new file mode 100644 index 0000000..2ed89ff Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/01.bmp new file mode 100644 index 0000000..bb3e6dd Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/02.bmp new file mode 100644 index 0000000..007250b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/03.bmp new file mode 100644 index 0000000..bf14966 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/04.bmp new file mode 100644 index 0000000..47254fe Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/05.bmp new file mode 100644 index 0000000..f587e57 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/06.bmp new file mode 100644 index 0000000..cfc3629 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/07.bmp new file mode 100644 index 0000000..6fcee4e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/08.bmp new file mode 100644 index 0000000..543c3f1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/09.bmp new file mode 100644 index 0000000..37ad2ca Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/10.bmp new file mode 100644 index 0000000..94eab40 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/11.bmp new file mode 100644 index 0000000..f3e2caa Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/12.bmp new file mode 100644 index 0000000..10c74dc Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/13.bmp new file mode 100644 index 0000000..b9bf5a5 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/14.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/14.bmp new file mode 100644 index 0000000..15b1a8f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/15.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/15.bmp new file mode 100644 index 0000000..0e2e48f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/16.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/16.bmp new file mode 100644 index 0000000..b145d2b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/17.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/17.bmp new file mode 100644 index 0000000..0a1a0c9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/18.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/18.bmp new file mode 100644 index 0000000..b458261 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/19.bmp b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/19.bmp new file mode 100644 index 0000000..28a2df7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/01.png new file mode 100644 index 0000000..dded16c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/02.png new file mode 100644 index 0000000..07cd399 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/03.png new file mode 100644 index 0000000..198d91e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/04.png new file mode 100644 index 0000000..6b2b343 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/05.png new file mode 100644 index 0000000..64af409 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/06.png new file mode 100644 index 0000000..c40e022 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/07.png new file mode 100644 index 0000000..8ddf3f8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/08.png new file mode 100644 index 0000000..bd1a550 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/09.png new file mode 100644 index 0000000..80e45a6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/10.png new file mode 100644 index 0000000..b571e6d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/11.png new file mode 100644 index 0000000..06c285d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/12.png new file mode 100644 index 0000000..7431ae7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/13.png new file mode 100644 index 0000000..3394ba0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/14.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/14.png new file mode 100644 index 0000000..5ccda1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/15.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/15.png new file mode 100644 index 0000000..70e34b4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/16.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/16.png new file mode 100644 index 0000000..d70b910 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/16.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/17.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/17.png new file mode 100644 index 0000000..494f9ef Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/17.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/18.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/18.png new file mode 100644 index 0000000..2cf7fd1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/18.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/19.png b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/19.png new file mode 100644 index 0000000..3b24d23 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/06_falling_star/koala/png/19.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia.csv b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia.csv new file mode 100644 index 0000000..d366d4a --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia.csv @@ -0,0 +1,9 @@ +50,lilia/lilia1.png +4,lilia/lilia2.png +4,lilia/lilia3.png +4,lilia/lilia4.png +4,lilia/lilia5.png +4,lilia/lilia6.png +4,lilia/lilia7.png +4,lilia/lilia8.png +250,lilia/lilia9.png diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia1.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia1.png new file mode 100644 index 0000000..a17fb75 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia1.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia2.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia2.png new file mode 100644 index 0000000..b5bc164 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia2.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia3.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia3.png new file mode 100644 index 0000000..b9187b9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia3.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia4.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia4.png new file mode 100644 index 0000000..7a95c3e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia4.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia5.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia5.png new file mode 100644 index 0000000..a321f81 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia5.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia6.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia6.png new file mode 100644 index 0000000..953589c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia6.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia7.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia7.png new file mode 100644 index 0000000..e36f1d0 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia7.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia8.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia8.png new file mode 100644 index 0000000..adca640 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia8.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia9.png b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia9.png new file mode 100644 index 0000000..aa59315 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/07_lilia/lilia9.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/01.bmp b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/01.bmp new file mode 100644 index 0000000..8a4df67 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/01.bmp new file mode 100644 index 0000000..a1b9cd9 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/png/01.png new file mode 100644 index 0000000..0ba6742 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/08_lilia_hero/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/01.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/01.bmp new file mode 100644 index 0000000..3f130de Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/02.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/02.bmp new file mode 100644 index 0000000..738cbb1 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/03.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/03.bmp new file mode 100644 index 0000000..763dcce Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/04.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/04.bmp new file mode 100644 index 0000000..68d679b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/05.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/05.bmp new file mode 100644 index 0000000..b5f7d86 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/06.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/06.bmp new file mode 100644 index 0000000..544db82 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/07.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/07.bmp new file mode 100644 index 0000000..eb6aa2b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/08.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/08.bmp new file mode 100644 index 0000000..1b87a02 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/09.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/09.bmp new file mode 100644 index 0000000..2c23cd4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/10.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/10.bmp new file mode 100644 index 0000000..06b670e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/11.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/11.bmp new file mode 100644 index 0000000..8be2b0d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/12.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/12.bmp new file mode 100644 index 0000000..208ffec Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/13.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/13.bmp new file mode 100644 index 0000000..f9822f4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/14.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/14.bmp new file mode 100644 index 0000000..999e70a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/15.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/15.bmp new file mode 100644 index 0000000..4acbbb8 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/16.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/16.bmp new file mode 100644 index 0000000..a301bc7 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-prepared.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-prepared.png new file mode 100644 index 0000000..faf8e1d Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-prepared.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-result.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-result.png new file mode 100644 index 0000000..b330a84 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-result.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-speedcode.txt b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-speedcode.txt new file mode 100644 index 0000000..d1b6e31 --- /dev/null +++ b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee-speedcode.txt @@ -0,0 +1,211 @@ +0* | 0 | 0fff | | 2 | free cc: 36/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=68 y=3a | V_18=X; A=58; X=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +1 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=58 x=3c y=3a | V_18=A; A=48; Y=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +2 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3c y=3e | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +3 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3c y=3e | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +4* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3c y=3e | A=28; V_18=A; A=3a; X=18; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +5 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +6 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +7 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +8* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +9 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +10 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +11 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +12* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +13 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +14 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +15 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +16* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +17 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +18 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +19 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +20* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +21 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +22 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +23 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +24* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +25 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +26 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +27 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +28* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +29 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +30 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +31 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +32* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +33 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +34 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +35 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +36* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +37 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +38 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +39 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +40* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +41 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +42 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +43 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +44* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +45 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +46 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +47 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +48* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +49 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +50 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +51 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +52* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +53 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:18[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=18 y=3c | V_18=X; A=08; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +54 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:08[10-55] d011:3e[58-58] + | | | 000000 | | a=08 x=3e y=3c | V_18=A; A=78; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +55 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:78[10-55] d011:38[10-55] + | | | 000000 | | a=78 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +56* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:68[10-55] d011:3a[58-58] + | | | 000000 | | a=78 x=3e y=38 | A=68; V_18=A; A=3a; X=58; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +57 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:58[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=58 y=3c | V_18=X; A=48; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +58 | 0 | 0fff | 000000 | 2 | free cc: 38/59 | d018:48[10-55] d011:3e[58-58] + | | | 000000 | | a=48 x=3e y=3c | V_18=A; A=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +59 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:38[10-55] d011:38[10-55] + | | | 000000 | | a=38 x=3e y=3c | V_18=A; V_11=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +60* | 0 | 0fff | 000000 | 2 | free cc: 35/59 | d018:28[10-55] d011:3a[58-58] + | | | 000000 | | a=38 x=3e y=3c | A=28; V_18=A; A=3a; X=18; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +61 | 0 | 0fff | 000000 | 8 | free cc: 10/59 | d018:18[10-55] d001:d4[10-55] d003:d4[10-55] d005:d4[10-55] d007:d4[10-55] d009:d4[10-55] d00b:d4[10-55] d011:3c[58-58] + | | | 000000 | +6 | a=3a x=18 y=3c | V_18=X; A=d4; V_01=A; V_03=A; V_05=A; V_07=A; V_09=A; V_0b=A; A=08; X=3e; N; N; N; N; N; V_11=Y +62 | 0 | 0fff | 000000 | 4 | free cc: 26/59 | d018:08[10-55] d00d:d4[10-55] d00f:d4[10-55] d011:3e[58-58] + | | | 000000 | +2 | a=08 x=3e y=3c | V_18=A; A=d4; V_0d=A; V_0f=A; A=ffffff03; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +63 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | dd00:ffffff03[10-55] d011:38[10-55] + | | | 000000 | | a=03 x=3e y=38 | V_00=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +64* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:98[10-55] d011:3a[58-58] + | | | 000000 | | a=03 x=3e y=38 | A=98; V_18=A; A=3a; X=a8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +65 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:a8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=a8 y=3c | V_18=X; A=b8; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +66 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3e[58-58] + | | | 000000 | | a=b8 x=3e y=3c | V_18=A; A=88; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +67 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:88[10-55] d011:38[10-55] + | | | 000000 | | a=88 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +68* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:98[10-55] d011:3a[58-58] + | | | 000000 | | a=88 x=3e y=38 | A=98; V_18=A; A=3a; X=a8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +69 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:a8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=a8 y=3c | V_18=X; A=b8; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +70 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3e[58-58] + | | | 000000 | | a=b8 x=3e y=3c | V_18=A; A=88; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +71 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:88[10-55] d011:38[10-55] + | | | 000000 | | a=88 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +72* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:98[10-55] d011:3a[58-58] + | | | 000000 | | a=88 x=3e y=38 | A=98; V_18=A; A=3a; X=a8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +73 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:a8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=a8 y=3c | V_18=X; A=b8; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +74 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3e[58-58] + | | | 000000 | | a=b8 x=3e y=3c | V_18=A; A=88; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +75 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:88[10-55] d011:38[10-55] + | | | 000000 | | a=88 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +76* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:98[10-55] d011:3a[58-58] + | | | 000000 | | a=88 x=3e y=38 | A=98; V_18=A; A=3a; X=a8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +77 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:a8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=a8 y=3c | V_18=X; A=b8; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +78 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3e[58-58] + | | | 000000 | | a=b8 x=3e y=3c | V_18=A; A=88; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +79 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:88[10-55] d011:38[10-55] + | | | 000000 | | a=88 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +80* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:98[10-55] d011:3a[58-58] + | | | 000000 | | a=88 x=3e y=38 | A=98; V_18=A; A=3a; X=a8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +81 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:a8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=a8 y=3c | V_18=X; A=b8; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +82 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3e[58-58] + | | | 000000 | | a=b8 x=3e y=3c | V_18=A; A=98; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +83 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:98[10-55] d011:38[10-55] + | | | 000000 | | a=98 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +84* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:a8[10-55] d011:3a[58-58] + | | | 000000 | | a=98 x=3e y=38 | A=a8; V_18=A; A=3a; X=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +85 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=b8 y=3c | V_18=X; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +86 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 000000 | | a=88 x=3e y=3c | V_18=A; A=98; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +87 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:98[10-55] d011:38[10-55] + | | | 000000 | | a=98 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +88* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:a8[10-55] d011:3a[58-58] + | | | 000000 | | a=98 x=3e y=38 | A=a8; V_18=A; A=3a; X=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +89 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=b8 y=3c | V_18=X; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +90 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 000000 | | a=88 x=3e y=3c | V_18=A; A=98; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +91 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:98[10-55] d011:38[10-55] + | | | 000000 | | a=98 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +92* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:a8[10-55] d011:3a[58-58] + | | | 000000 | | a=98 x=3e y=38 | A=a8; V_18=A; A=3a; X=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +93 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=b8 y=3c | V_18=X; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +94 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 000000 | | a=88 x=3e y=3c | V_18=A; A=98; Y=38; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +95 | 0 | 0fff | 000000 | 2 | free cc: 36/55 | d018:98[10-55] d011:38[10-55] + | | | 000000 | | a=98 x=3e y=38 | V_18=A; V_11=Y; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N +96* | 0 | 0fff | 000000 | 2 | free cc: 33/59 | d018:a8[10-55] d011:3a[58-58] + | | | 000000 | | a=98 x=3e y=38 | A=a8; V_18=A; A=3a; X=b8; Y=3c; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; *N; V_11=A +97 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:b8[10-55] d011:3c[58-58] + | | | 000000 | | a=3a x=b8 y=3c | V_18=X; A=88; X=3e; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +98 | 0 | 0fff | 000000 | 2 | free cc: 36/59 | d018:88[10-55] d011:3e[58-58] + | | | 000000 | | a=88 x=3e y=3c | V_18=A; A=18; Y=10; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=X +99 | 0 | 0fff | 000000 | 2 | free cc: 38/57 | d018:18[10-55] d011:10[56-56] + | | | | | a=18 x=3e y=10 | V_18=A; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; N; V_11=Y +free cycles = 3524 + +Resolutions: +Included 61 SpriteY d001:d4/0a-37/123-149 +Included 61 SpriteY d003:d4/0a-37/123-151 +Included 61 SpriteY d005:d4/0a-37/123-153 +Included 61 SpriteY d007:d4/0a-37/123-155 +Included 61 SpriteY d009:d4/0a-37/123-157 +Included 61 SpriteY d00b:d4/0a-37/123-159 +Included 62 SpriteY d00d:d4/0a-37/123-161 +Included 62 SpriteY d00f:d4/0a-37/123-163 diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.png new file mode 100644 index 0000000..b6939fb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.prg b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.prg new file mode 100644 index 0000000..ab8374b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/happee.prg differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/01.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/01.bmp new file mode 100644 index 0000000..8b0746f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/02.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/02.bmp new file mode 100644 index 0000000..67fda35 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/03.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/03.bmp new file mode 100644 index 0000000..4b22f0f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/04.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/04.bmp new file mode 100644 index 0000000..3cd9c43 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/05.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/05.bmp new file mode 100644 index 0000000..6edaed4 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/06.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/06.bmp new file mode 100644 index 0000000..b9ff83c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/07.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/07.bmp new file mode 100644 index 0000000..f59b472 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/08.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/08.bmp new file mode 100644 index 0000000..dfb7ac2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/09.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/09.bmp new file mode 100644 index 0000000..9730328 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/10.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/10.bmp new file mode 100644 index 0000000..57f0c1e Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/11.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/11.bmp new file mode 100644 index 0000000..b546e8a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/12.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/12.bmp new file mode 100644 index 0000000..7358307 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/13.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/13.bmp new file mode 100644 index 0000000..a6ad68c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/14.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/14.bmp new file mode 100644 index 0000000..a2e293f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/15.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/15.bmp new file mode 100644 index 0000000..560cb07 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/16.bmp b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/16.bmp new file mode 100644 index 0000000..ff406ca Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/01.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/01.png new file mode 100644 index 0000000..01d86ab Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/02.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/02.png new file mode 100644 index 0000000..c4bd291 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/03.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/03.png new file mode 100644 index 0000000..25d1c74 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/04.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/04.png new file mode 100644 index 0000000..079f560 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/05.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/05.png new file mode 100644 index 0000000..cedf511 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/06.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/06.png new file mode 100644 index 0000000..1849eba Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/07.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/07.png new file mode 100644 index 0000000..fab5afb Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/08.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/08.png new file mode 100644 index 0000000..d8e41fe Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/09.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/09.png new file mode 100644 index 0000000..319589b Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/10.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/10.png new file mode 100644 index 0000000..2497dd6 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/11.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/11.png new file mode 100644 index 0000000..0c6348a Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/12.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/12.png new file mode 100644 index 0000000..20677ad Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/13.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/13.png new file mode 100644 index 0000000..cb0f1e2 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/14.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/14.png new file mode 100644 index 0000000..16fc89f Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/15.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/15.png new file mode 100644 index 0000000..2bf2d9c Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/16.png b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/16.png new file mode 100644 index 0000000..e465593 Binary files /dev/null and b/loader/samples/minexample/png2prg 2/ys2/11_lilia_final/koala/png/16.png differ diff --git a/loader/samples/minexample/png2prg.7z b/loader/samples/minexample/png2prg.7z new file mode 100644 index 0000000..43a9155 Binary files /dev/null and b/loader/samples/minexample/png2prg.7z differ diff --git a/loader/samples/minexample/png2prg/01_badguyz_cut.csv b/loader/samples/minexample/png2prg/01_badguyz_cut.csv new file mode 100644 index 0000000..281948e --- /dev/null +++ b/loader/samples/minexample/png2prg/01_badguyz_cut.csv @@ -0,0 +1,29 @@ + +5,ys2/01_badguyz/koala/png/01.png +5,ys2/01_badguyz/koala/png/02.png +5,ys2/01_badguyz/koala/png/03.png +5,ys2/01_badguyz/koala/png/04.png +5,ys2/01_badguyz/koala/png/05.png +50,ys2/01_badguyz/koala/png/06.png + +15,ys2/01_badguyz/koala/png/08.png + +15,ys2/01_badguyz/koala/png/10.png + +100,ys2/01_badguyz/koala/png/12.png +15,ys2/01_badguyz/koala/png/10.png + +15,ys2/01_badguyz/koala/png/08.png + +100,ys2/01_badguyz/koala/png/06.png +5,ys2/01_badguyz/koala/png/13.png +5,ys2/01_badguyz/koala/png/14.png +5,ys2/01_badguyz/koala/png/15.png +5,ys2/01_badguyz/koala/png/16.png +5,ys2/01_badguyz/koala/png/17.png +5,ys2/01_badguyz/koala/png/18.png +5,ys2/01_badguyz/koala/png/19.png +100,ys2/01_badguyz/koala/png/00.png + + + diff --git a/loader/samples/minexample/png2prg/02_tower.csv b/loader/samples/minexample/png2prg/02_tower.csv new file mode 100644 index 0000000..8e7837c --- /dev/null +++ b/loader/samples/minexample/png2prg/02_tower.csv @@ -0,0 +1,19 @@ +100,ys2/03_tower/koala/png/01.png +5,ys2/03_tower/koala/png/02.png +5,ys2/03_tower/koala/png/03.png +5,ys2/03_tower/koala/png/04.png +5,ys2/03_tower/koala/png/05.png +5,ys2/03_tower/koala/png/06.png +50,ys2/03_tower/koala/png/03.png +15,ys2/03_tower/koala/png/07.png +15,ys2/03_tower/koala/png/08.png +15,ys2/03_tower/koala/png/09.png +15,ys2/03_tower/koala/png/10.png +15,ys2/03_tower/koala/png/11.png +15,ys2/03_tower/koala/png/12.png +15,ys2/03_tower/koala/png/13.png +15,ys2/03_tower/koala/png/14.png +15,ys2/03_tower/koala/png/15.png + + + diff --git a/loader/samples/minexample/png2prg/02_tower.prg b/loader/samples/minexample/png2prg/02_tower.prg new file mode 100644 index 0000000..09b6a78 Binary files /dev/null and b/loader/samples/minexample/png2prg/02_tower.prg differ diff --git a/loader/samples/minexample/png2prg/03_tower.csv b/loader/samples/minexample/png2prg/03_tower.csv new file mode 100644 index 0000000..d171204 --- /dev/null +++ b/loader/samples/minexample/png2prg/03_tower.csv @@ -0,0 +1,20 @@ +100,ys2/03_tower/koala/png/01.png +5,ys2/03_tower/koala/png/02.png +5,ys2/03_tower/koala/png/03.png +5,ys2/03_tower/koala/png/04.png +5,ys2/03_tower/koala/png/05.png +5,ys2/03_tower/koala/png/06.png +5,ys2/03_tower/koala/png/03.png +50,ys2/03_tower/koala/png/01.png +15,ys2/03_tower/koala/png/07.png +15,ys2/03_tower/koala/png/08.png +15,ys2/03_tower/koala/png/09.png +15,ys2/03_tower/koala/png/10.png +15,ys2/03_tower/koala/png/11.png + +15,ys2/03_tower/koala/png/13.png + +150,ys2/03_tower/koala/png/15.png + + + diff --git a/loader/samples/minexample/png2prg/03_tower.prg b/loader/samples/minexample/png2prg/03_tower.prg new file mode 100644 index 0000000..ea7027e Binary files /dev/null and b/loader/samples/minexample/png2prg/03_tower.prg differ diff --git a/loader/samples/minexample/png2prg/examples.d64 b/loader/samples/minexample/png2prg/examples.d64 new file mode 100644 index 0000000..692f4d3 Binary files /dev/null and b/loader/samples/minexample/png2prg/examples.d64 differ diff --git a/loader/samples/minexample/png2prg/lilia.csv b/loader/samples/minexample/png2prg/lilia.csv new file mode 100644 index 0000000..c35d31c --- /dev/null +++ b/loader/samples/minexample/png2prg/lilia.csv @@ -0,0 +1,38 @@ +100,01_badguyz/koala/png/png/00.png +5,01_badguyz/koala/png/png/01.png +5,01_badguyz/koala/png/02.png +5,01_badguyz/koala/png/03.png +5,01_badguyz/koala/png/04.png +5,01_badguyz/koala/png/05.png +50,01_badguyz/koala/png/06.png +15,01_badguyz/koala/png/07.png +15,01_badguyz/koala/png/08.png +15,01_badguyz/koala/png/09.png +15,01_badguyz/koala/png/10.png +15,01_badguyz/koala/png/11.png +100,01_badguyz/koala/png/12.png +15,01_badguyz/koala/png/10.png +15,01_badguyz/koala/png/09.png +15,01_badguyz/koala/png/08.png +15,01_badguyz/koala/png/07.png +100,01_badguyz/koala/png/06.png +5,01_badguyz/koala/png/13.png +5,01_badguyz/koala/png/14.png +5,01_badguyz/koala/png/15.png +5,01_badguyz/koala/png/16.png +5,01_badguyz/koala/png/17.png +5,01_badguyz/koala/png/18.png +5,01_badguyz/koala/png/19.png +100,01_badguyz/koala/png/00.png + +150,02_title,title_clashstate.png + +50,lilia/lilia1.png +4,lilia/lilia2.png +4,lilia/lilia3.png +4,lilia/lilia4.png +4,lilia/lilia5.png +4,lilia/lilia6.png +4,lilia/lilia7.png +4,lilia/lilia8.png +250,lilia/lilia9.png diff --git a/loader/samples/minexample/png2prg/lilia.prg b/loader/samples/minexample/png2prg/lilia.prg new file mode 100644 index 0000000..bc84828 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia.prg differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia.csv b/loader/samples/minexample/png2prg/lilia/lilia.csv new file mode 100644 index 0000000..d366d4a --- /dev/null +++ b/loader/samples/minexample/png2prg/lilia/lilia.csv @@ -0,0 +1,9 @@ +50,lilia/lilia1.png +4,lilia/lilia2.png +4,lilia/lilia3.png +4,lilia/lilia4.png +4,lilia/lilia5.png +4,lilia/lilia6.png +4,lilia/lilia7.png +4,lilia/lilia8.png +250,lilia/lilia9.png diff --git a/loader/samples/minexample/png2prg/lilia/lilia1.png b/loader/samples/minexample/png2prg/lilia/lilia1.png new file mode 100644 index 0000000..e63a1bd Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia1.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia2.png b/loader/samples/minexample/png2prg/lilia/lilia2.png new file mode 100644 index 0000000..013a981 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia2.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia3.png b/loader/samples/minexample/png2prg/lilia/lilia3.png new file mode 100644 index 0000000..5001341 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia3.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia4.png b/loader/samples/minexample/png2prg/lilia/lilia4.png new file mode 100644 index 0000000..56ee1b9 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia4.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia5.png b/loader/samples/minexample/png2prg/lilia/lilia5.png new file mode 100644 index 0000000..1b9662a Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia5.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia6.png b/loader/samples/minexample/png2prg/lilia/lilia6.png new file mode 100644 index 0000000..8d55638 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia6.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia7.png b/loader/samples/minexample/png2prg/lilia/lilia7.png new file mode 100644 index 0000000..06db685 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia7.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia8.png b/loader/samples/minexample/png2prg/lilia/lilia8.png new file mode 100644 index 0000000..9f6fa29 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia8.png differ diff --git a/loader/samples/minexample/png2prg/lilia/lilia9.png b/loader/samples/minexample/png2prg/lilia/lilia9.png new file mode 100644 index 0000000..4c9e235 Binary files /dev/null and b/loader/samples/minexample/png2prg/lilia/lilia9.png differ diff --git a/loader/samples/minexample/png2prg/png2prg_darwin_arm64 b/loader/samples/minexample/png2prg/png2prg_darwin_arm64 new file mode 100755 index 0000000..f36afcf Binary files /dev/null and b/loader/samples/minexample/png2prg/png2prg_darwin_arm64 differ diff --git a/loader/samples/minexample/png2prg/png2prg_win_amd64.exe b/loader/samples/minexample/png2prg/png2prg_win_amd64.exe new file mode 100644 index 0000000..5ba399f Binary files /dev/null and b/loader/samples/minexample/png2prg/png2prg_win_amd64.exe differ diff --git a/loader/samples/minexample/png2prg/png2prg_win_x86.exe b/loader/samples/minexample/png2prg/png2prg_win_x86.exe new file mode 100644 index 0000000..0b547db Binary files /dev/null and b/loader/samples/minexample/png2prg/png2prg_win_x86.exe differ diff --git a/loader/samples/minexample/png2prg/readme.md b/loader/samples/minexample/png2prg/readme.md new file mode 100644 index 0000000..ad40513 --- /dev/null +++ b/loader/samples/minexample/png2prg/readme.md @@ -0,0 +1,687 @@ +# PNG2PRG 1.12 by burg + +Png2prg converts a 320x200 image (png/gif/jpeg) to a c64 hires or +multicolor bitmap, charset, petscii, ecm or sprites prg. It will find the best +matching palette and background/bitpair-colors automatically, no need to modify +your source images or configure a palette. + +Vice screenshots with default borders (384x272) are automatically cropped. +Vice's main screen offset is at x=32, y=35. +Quite a few people (and possibly tools too) use the incorrect 32,36 offset. +Use the -alt-offset or -ao flag to use 32,36 as offset. +Images in sprite dimensions will be converted to sprites. + +The resulting .prg includes the 2-byte start address and optional displayer. +The displayers can optionally play a .sid tune. + +This tool can be used in all buildchains on all common platforms. + +## What Is New + +Png2prg 1.12 introduces animation.csv support for custom delays per frame. +See 'Animation csv' below for details. +The -no-loop flag causes animations to only display once. + +This release contains an important bugfix related to mixedcharsets, +where in some cases, png2prg would require more unique chars than necessary. +There were more issues with png2prg 1.10, which were hotfixed in 1.10.1. +It is best to delete any 1.10 version and upgrade to 1.12. + +ECM conversion has been improved, now png2prg also searches for potential +char reduction by searching for invertable characters. + +Trident added [devcontainer files](https://github.com/staD020/png2prg/commit/6cb6c48a2804fa5210cf704e0af4cff3313398fe) for setting up a Docker +development environment to compile png2prg directly from within VSCode. + +See 'Changes for version 1.12' below for more features and details. + +## What it is *not* + +Png2prg is not a tool to wire fullcolor images. It needs input images to +already be compliant with c64 color and size restrictions. +In verbose mode (-v) it outputs locations of color clashes, if any. + +If you do need to wire fullcolor images, check out Youth's [Retropixels](https://www.micheldebree.nl/retropixels/). + +## Supported Graphics Modes + + koala: multicolor bitmap (max 4 colors per char) + hires: singlecolor bitmap (max 2 colors per char) + mixedcharset: multicolor charset (max 4 colors per char (fixed bgcol, d022, d023)) + mccharset: multicolor charset (max 4 colors) + sccharset: singlecolor charset (max 2 colors per char (fixed bgcol)) + petscii: singlecolor rom charset (max 2 colors per char (fixed bgcol)) + ecm: singlecolor charset (max 2 colors per char (4 fixed bgcolors), max 64 chars) + mcsprites: multicolor sprites (max 4 colors) + scsprites: singlecolor sprites (max 2 colors) + mcibitmap: 320x200 multicolor interlace bitmap (max 4 colors per char/frame) + +Png2prg is mostly able to autodetect the correct graphics mode, but you can +also force a specific graphics mode with the -mode flag: + + ./png2prg -m koala image.png + +## Koala or Hires Bitmap + + Bitmap: $2000 - $3f3f + Screen: $3f40 - $4327 + D020: $4328 (singlecolor only) + D800: $4328 - $470f (multicolor only) + D021: $4710 (multicolor only, low-nibble) + D020: $4710 (multicolor only, high-nibble) + +## Multicolor Interlace Bitmap + +You can supply one 320x200 multicolor image with max 4 colors per 8x8 pixel +char per frame of which at least 2 are shared (the D021 and D800 colors). + +Or supply both frames in regular koala specs (-interlace flag required). +When making screenshots in vice, please disable the d016 pixel shift manually. + + ./png2prg -i testdata/madonna/frame_0.png testdata/madonna/frame_1.png + +### Drazlace (shared screenram and colorram for both frames) + + ./png2prg testdata/madonna/cjam_pure_madonna.png + + D800: $5800 - $5be7 + Screen: $5c00 - $5fe7 + Bitmap1: $6000 - $7f3f + D021: $7f40 (low-nibble) + D020: $7f40 (high-nibble) + D016Offset: $7f42 + Bitmap2: $8000 - $9f3f + +### Multicolor Interlace (shared colorram, true paint .mci format) + + ./png2prg -i -d016 1 testdata/mcinterlace/parriot?.png + + Screen1: $9c00 - $9fe7 + D021: $9fe8 (low-nibble) + D020: $9fe8 (high-nibble) + D016Offset: $9fe9 + Bitmap1: $a000 - $bf3f + Bitmap2: $c000 - $df3f + Screen2: $e000 - $e3e7 + D800: $e400 - $e7e7 + +## Singlecolor, PETSCII or ECM Charset (individual d800 colors) + +By default charsets are packed, they only contain unique characters. +If you do not want charpacking, eg for a 1x1 charset, please use -no-pack. + +With ECM -bitpair-colors can be used to force d021-d024 colors. + +NB: individual d800 colors are not supported with -no-pack. + + ./png2prg -m sccharset testdata/hirescharset/ohno_logo.png + ./png2prg -m petscii testdata/petscii/hein_hibiscus.png + ./png2prg -m ecm testdata/ecm/xpardey.png + ./png2prg -m ecm testdata/ecm/shampoo.png + ./png2prg -m ecm -bpc 2,7,14,0 testdata/ecm/orion.png + + Charset: $2000-$27ff (omitted for petscii) + Screen: $2800-$2be7 + D800: $2c00-$2fe7 + D020: $2fe8 + D021: $2fe9 + D022: $2fea (ecm only) + D023: $2feb (ecm only) + D024: $2fec (ecm only) + +## Mixed Multi/Singlecolor Charset (individual d800 colors) + +Png2prg tries to figure out the right -bitpair-colors and auto-corrects +where it can, but there still are edge-cases like the ones below. +If an impossible color is found, an error will be displayed. +Swap some -bpc colors around and retry. +There can also be cases where manual -bpc colors can influence char-count or +packed size. + +You may want to add the -brute-force flag so most color options will be tried. +The best packed result wins, not necessarily the version with the least amount +of chars. + + ./png2prg -m mixedcharset testdata/mixedcharset/hein_neo.png + ./png2prg -m mixedcharset testdata/mixedcharset/huntress.gif + ./png2prg -m mixedcharset -bpc 3 testdata/mixedcharset/shine.png + ./png2prg -m mixedcharset -bpc 0 testdata/mixedcharset/charsetcompo.png + + Charset: $2000-$27ff + Screen: $2800-$2be7 + D800: $2c00-$2fe7 + D020: $2fe8 + D021: $2fe9 + D022: $2fea + D023: $2feb + +## Single or Multicolor Sprites + +If the source image size is a multiple of a 24x21 pixel sprite, +the image is considered to contain sprites. + +The image will be converted from left to right, top to bottom. + + ./png2prg image.png + ./png2prg -m scsprites image.png + ./png2prg -m mcsprites image.png + + Sprite 1: $2000-$203f + Sprite 2: $2040-$207f + ... + +## Bitpair Colors + +By default, png2prg guesses bitpair colors by itself. In most cases you +don't need to configure anything. It will provide a mostly normalized image +which should yield good pack results, but your miles may vary. + +To give you more control, you can force/prefer a specific bitpair +color-order. Use c64 colors, so 0 for black, 1 for white, 2 for red, etc. + +The following example will force background color 0 for bitpair 00 and +prefer colors 6,14,3 for bitpairs 01,10,11: + + ./png2prg -bitpair-colors 0,6,14,3 image.png + +It's also possible to explicitly skip certain bitpair preferences with -1: + + ./png2prg -bitpair-colors 0,-1,-1,3 image.png + +## Animations + +When multiple files are added, they are treated as animation frames. +You can also supply an animated .gif. + +## Sprite Animation + +Each frame will be concatenated in the output .prg. + +## Bitmap Animation (only koala and hires) + +Note that png2prg uses a rather simple generic diff approach, where small +changes frame by frame work well. Trying to change large areas at once +is not advised. +Use the -no-fade flag if you run out of memory. + +The first image will be exported with all framedata appended. +Koala animation frames start at $4711, hires at $4329. + +The frame files are following this format. +Each frame consists of 1 or more chunks. A chunk looks like this: + + .byte $03 // number of chars in this chunk + // $00 marks end of frame + // $ff marks end of all frames + .word bitmap // bitmap address of this chunk (the high byte is <$20) + .word screen // screenram address (the high byte is <$04) + + For each char in this chunk: + + .byte 0,31,15,7,8,34,0,128 // pixels + .byte $64 // screenram colors + .byte $01 // colorram color (koala only) + ... // next char(s) + + ... // next chunks + .byte 0 // end of frame + .byte 6 // wait for 6 frames + ... // next frame(s) + .byte $ff // end of all frames + +## PETSCII and Charset Animation + +Only petscii and sccharset modes support different background and +bordercolors per frame. +All chars used in all frames must fit into a single 256 char charset. + +Each frame consists of 1 or more chunks. A chunk looks like this: + + .byte $xy // $y = bgcol, $x = bordercol (only for petscii/sccharset) + .byte $03 // number of chars in this chunk + // $00 marks end of frame + // $ff marks end of all frames + .word screen // screenram address (the high byte is <$04) + + For each char in this chunk: + + .byte $03 // character + .byte $01 // colorram color + ... // next char(s) + + ... // next chunks + .byte 0 // end of frame + .byte 6 // wait for 6 frames + ... // next frame(s) + .byte $ff // end of all frames + +## Animation csv + +Since version 1.12 animation.csv support has been added to give more +freedom to users wanting to create animation displayers. +It is now possible to use a custom frame-delay per frame. + +The csv should contain rows of frame delay value and image, where the +delay can be any value from 0 till 255. The highest delay is a little +over 5 seconds on PAL systems. +If you want longer delays, just copy a row. + + 10,frame0.png + 50,frame1.png + 10,frame2.png + +Examples can be found here: [Évoluer by The Sarge](https://github.com/staD020/png2prg/blob/master/testdata/evoluer/evoluer.csv) and [Rose by Sander](https://github.com/staD020/png2prg/blob/master/testdata/petscii/anim/rose.csv) + + png2prg -d -o evoluer.prg -sid testdata/evoluer/Evoluer.sid testdata/evoluer/evoluer.csv + png2prg -d -o rose.prg testdata/petscii/anim/rose.csv + +## Displayer + +The -d or -display flag will link displayer code infront of the picture. +By default it will also crunch the resulting file with Antonio Savona's +[TSCrunch](https://github.com/tonysavon/TSCrunch/) with a couple of changes in my own [fork](https://github.com/staD020/TSCrunch/). + +All displayers except for sprites support adding a .sid. +Multispeed sids are supported as long as the .sid initializes the CIA timers +correctly. + +You can use sids located from $0e00-$1fff or $e000+ in the displayers. +More areas may be free depending on graphics type. +A memory usage map is shown on error and in -vv (very verbose) mode. + +If needed, you can relocate most sids using lft's [sidreloc](http://www.linusakesson.net/software/sidreloc/index.php). + +Zeropages $08-$0f are used in the animation displayers, while none are used +in hires/koala displayers, increasing sid compatibility. + +## Brute Force Mode and Pack Optimization + +By default png2prg 1.8 does a pretty good job at optimizing the resulting prg +for crunchers and packers. It is not enough to beat [SPOT 1.3](https://csdb.dk/release/?id=242492). + +The optimization techniques used by png2prg are also responsible for cleaning +up the bitmap, making it ideal for animations and color effects. + +### -brute-force (-bf) + +Iterates are over many -bitpair-colors permutations automatically, packs +with the built in TSCrunch and selects the shortest. + + ./png2prg -bf image.png + +The -brute-force mode can be used in combination with additional flags. + +### -no-bitpair-counters (-nbc) + +Disable counting of bitpairs per color to guess a bitpair for a color. + + ./png2prg -bf -nbc image.png + +### -no-prev-char-colors (-npcc) + +Disable lookback to previous char's charcolors to guess a bitpair for a color. + + ./png2prg -bf -npcc image.png + +Since TSCrunch is optimized for speed, packing with Dali can give varying +results. This is also the reason for not including these options in the +brute force permutations automatically. + +## Benchmark + +The [koala otpimizing thread](https://csdb.dk/forums/?roomid=13&topicid=38311&showallposts=1) on csdb has gained some interest in the scene. +Since Sparta released [SPOT](https://csdb.dk/release/?id=242492) it has been the best optimizer available. + +Png2prg 1.8 has improved optimization techniques but requires -brute-force +mode to beat SPOT 1.3. Manual flags can optimize even better in some cases. + +All koalas are packed with [Dali 0.3.2](https://csdb.dk/release/?id=223584). + + +---------+--------+----------+------------+--------+ + | spot1.3 | p2p1.8 | p2p1.8bf | p2p1.8best | p2p1.6 | + +---------+--------+----------+------------+--------+ + | 7332 | 7372 | 7332 | 7324 | 7546 | Untitled/Floris + | 5136 | 5190 | 5149 | bf | 5464 | Song of the Sunset/Mermaid + | 5968 | 5998 | 5963 | bf | 6155 | Short Circuit/Karen Davies + | 3618 | 3647 | 3616 | 3589 | 3830 | Portrait L+D/Sander + | 5094 | 5080 | 5083 | 5078 | 5320 | Weee/Mermaid + | 7497 | 7471 | 7458 | bf | 7612 | Deadlock/Robin Levy + | 8068 | 8097 | 8046 | 8038 | 8227 | Room with a view/Veto + | 7445 | 7490 | 7432 | bf | 7582 | Vangelis/Talent + | 6759 | 6739 | 6737 | bf | 6963 | Temple of Tears/Hend + | 7859 | 7848 | 7839 | 7821 | 7998 | Thanos/JonEgg + | 4859 | 4849 | 4782 | bf | 4983 | Solar-Sonar/Leon + | 5640 | 5671 | 5613 | bf | 5869 | Cisco Heat/Alan Grier + | 6243 | 6286 | 6228 | bf | 6430 | Daylight/Sulevi + | 2850 | 2884 | 2848 | bf | 3092 | Yie Ar Kung Fu/Steve Wahid + | 6727 | 6721 | 6730 | 6711 | 6901 | Lee/The Sarge + | 7837 | 7828 | 7798 | bf | 7960 | Parrot/Mirage + | 4559 | 4536 | 4494 | bf | 4821 | Dragon's Lair + | 4275 | 4324 | 4292 | 4284 | 4519 | Scorpion/SIR'88 + | 5562 | 5558 | 5506 | bf | 5668 | Hatching/Joe + +---------+--------+----------+------------+--------+ + | 113328 | 113589 | 112946 | 112853 | 116940 | Total + +---------+--------+----------+------------+--------+ + + - p2p1.8: default png2prg result w/o options + - p2p1.8bf: -brute-force mode + - p2p1.8best: hand-picked -bitpair-colors, or bruteforced with -npcc and/or -nbc flags + - p2p1.6: default png2prg 1.6 result w/o options + +## Examples + +This release contains examples with all assets included for you to test with. +Also included are the assets of [Évoluer](https://csdb.dk/release/?id=220170) by The Sarge and Flotsam. +A larger set of testdata can be found in the [github repo](https://github.com/staD020/png2prg/tree/master/testdata). + +## Install from source + +Png2prg was built on Linux, building on Mac should work out of the box. +For Windows, try out Windows Subsystem Linux (WSL), works pretty well. +However, natively building on Windows should be easy enough, look at +Compiling without Make below. + +The compiled displayer prgs are included in the repo to ease building +and importing png2prg as a library. Java is only required to build +the displayers with KickAssembler (included in the repo). + +But first [install Go 1.20 or higher](https://go.dev/dl/). + +### Simple install + + go install -v github.com/staD020/png2prg@master + +### Compiling with Make (recommended) + + git clone https://github.com/staD020/png2prg.git + cd png2prg + make -j + +Build for all common targets: + + make all -j + +### Compiling without Make + + go build ./cmd/png2prg + +## Install and use as library + +In your Go project's path, go get the library: + + go get github.com/staD020/png2prg + +In essence png2prg implements the [io.WriterTo](https://pkg.go.dev/io#WriterTo) interface. +Typical usage could look like below. A more complex example can be found +in the [source](https://github.com/staD020/png2prg/blob/master/cmd/png2prg/main.go) of the cli tool. + +```go +import ( + "fmt" + "io" + "github.com/staD020/png2prg" +) + +func convertPNG(w io.Writer, png io.Reader) (int64, error) { + p, err := png2prg.New(png2prg.Options{}, png) + if err != nil { + return 0, fmt.Errorf("png2prg.New failed: %w", err) + } + return p.WriteTo(w) +} +``` + +## Changes for version 1.12 + + - Bugfix: Fix regression with handling mixedcharsets that was increasing + char usage count in some cases (thanks Shine). + - Feature: Press CBM key in petscii (animation) displayers to switch + charset case. + - Feature: Add -brute-force support to all charset modes. + - Feature: Improve ECM handling by searching for invertable characters to + reduce char usage. + - Feature: Add animation.csv support to allow for custom delays per frame. + - Feature: Add -no-loop support for animations (thanks jab). + - Feature: Add -no-fade support to other displayers (thanks Shine). + - Feature: Disable repeating color optimization for koala & hires anims. + This reduces animation size & runtime processing at the cost of initial + image optimization. + - Feature: Allow sids to use all memory below $0400 (thanks kbs). + - Feature: VSCode Docker build support for a devcontainer was added by + Trident (thanks!). + - Experimental: Add secondary+tertiary preferred bitpair colors with -bpc2 + and -bpc3 (thanks Fungus). + +## Changes for version 1.10.1 + + - Play NTSC .sid tunes at the right speed (thanks Acrouzet). + - Bugfix: palette detection must detect all hires colors, also on odd pixels. + - Bugfix: dont look at border area to determine hires pixels. + - Bugfix: fix -frame-delay for charset anim displayers. + +## Changes for version 1.10 + + - Add gfxmode to .sym files and display in terminal output (thanks Spider-J). + - Add petscii animation support. + - Add background and bordercolor to each petscii or sccharset animation frame. + - Add -no-anim flag disable sc/mccharset animations and store frames as separate + screens. + - Add -no-fade flag for koala, hires, petscii and sccharset animation + displayers, this frees up a lot of RAM for animation data and sid. + - Code refactor, standardizing color and bitpair code, separated palettes in + palettes.yaml and more. + - Bugfix: repair -force-border-color. + - Bugfix: handle blank ECM images as well as ECM images using few bg colors + (thanks Brush). + - Typofix: fix simple install docs (thanks IcePic). + - Added another weird palette (thanks Fungus). + +## Changes for version 1.8 + + - Improve crunchiness by re-using the previous char's bitpair-colors. + - Add -no-prev-char-colors flag to disable re-use of the previous char's + bitpair-colors, in some cases this optimization causes worse pack results. + - Add -brute-force mode to find bitpair color combinations with better + crunchiness. Burns some CPU for a couple seconds. + - Add -no-bitpair-counters flag to disable using bitpair counters per color + for color guessing. + - Added multi-frame support for mccharset, where all frames use the same + charset. + - Add support for any centered fullscreen image resolution bigger than + 320x200 and other than 384x272. + - Add support for Marq's PETSCII tool .png resolution 352x232 (thanks jab). + - Bugfix: docs fixes related to installation from source (thanks jab). + - Bugfix: hide findECMColors log behind -verbose mode (thanks jab). + - Docs fix: add a bit more info for sprites (thanks fungus). + +## Changes for version 1.6 + + - Added -mode mixedcharset for mixed multicolor/singlecolor and + individual d800 colors per char. + - Modified -mode sccharset to use individual d800 colors per char. + - Added -mode petscii. + - Added -mode ecm. + - Added -no-pack-empty to skip packing empty chars to filled chars to re-use + for different colors. Only for mixed and ecm charsets. + - Added -force-pack-empty for singlecolor and multicolor charset, may save + a char, but usually pack-ratio is worse due to increased d800 color usage. + - Improved auto-detection of graphics modes, including various charset modes. + - Added sid support to charset displayers. + - Added fullscreen fade in/out to charset displayers. + - Bug Fix: -force-border-color for singlecolor charset (thanks Raistlin). + - Bug Fix: do not write empty .prg file on error. + - Standardized d02x colors in output.prg for charset modes. + +## Changes for version 1.4 + + - Support for even more far-out palette ranges (thanks Perplex). + - Now throws an error if the palette can't be detected properly, this should + never happen. Please let me know if you run into this error. + - Separated library and cli tool. + - Library supports the standard [io.Reader](https://pkg.go.dev/io#Reader) and [io.Writer](https://pkg.go.dev/io#Writer) interfaces. + - Patched [TSCrunch](https://github.com/staD020/TSCrunch/) further to increase crunch speed and use less memory. + - Added -parallel and -worker flags to treat each input file as standalone + and convert all files in parallel. Gifs with multiple frames are still + treated as animations. + - Stop relying on .gif filename extension, detect it. + - Add -alt-offset flag to force screenshot offset 32, 36), used by a few + graphicians. Though, please switch to the correct 32, 35. + - Add -symbols flag to write symbols to a .sym file. + - Interlace support for mcibitmap (drazlace and truepaint). + - Bugfix: allow blank images input (thanks Spider-J). + - Allow colors not present in the image as -bitpair-colors (thanks Map). + +## Changes for version 1.2 + + - Added displayer for koala animations. + - Added displayer for hires animations. + - Added -frame-delay flag for animation displayers. + - Added -wait-seconds flag for animation displayers. + - Fixed bug in koala/hires displayers not allowing sids to overlap $c000-$c7ff. + - Expanding wildcards: using pic??.png or pic*.png now also works on Windows. + - Set bank via $dd00 in displayers. + +## Changes for version 1.0 + + - Added fullscreen fade in/out to koala and hires displayers. + - Added optional .sid support for koala and hires displayers. + - Added optional crunching for all displayers using TSCrunch. + +## Credits + +Png2prg was created by Burglar, using the following third-party libraries: + +[TSCrunch 1.3](https://github.com/tonysavon/TSCrunch/) by Antonio Savona for optional crunching when exporting +an image with a displayer. + +[Colfade Doc](https://csdb.dk/release/?id=132276) by Veto for the color fade tables used in the displayers. + +[Kick Assembler](http://www.theweb.dk/KickAssembler/) by Slammer to compile the displayers. + +[Go](https://go.dev/) by The Go Authors is the programming language used to create png2prg. + +### Thanks to + +Apollyon, Spider-J, Brush, The Sarge, Fungus, Jab, Shine, Raistlin, Perplex, +Map, Youth, IcePic, Sander, Guinea Pig, Krill, Christopher Jam, Sparta, +Acrouzet, Trident, Worrior1 and Antonio Savona. + +## Options + +``` + -alt-offset + use alternate screenshot offset with x,y = 32,36 + -ao + alt-offset + -bf + brute-force + -bitpair-colors string + prefer these colors in 2bit space, eg 0,6,14,3 + -bpc string + bitpair-colors + -bpc2 string + secondary bitpair colors eg 0,2,10,7 + -bpc3 string + tertiary bitpair colors eg 0,11,12,15 + -brute-force + brute force bitpair-colors + -cpuprofile file + write cpu profile to file + -d display + -d016 int + d016offset (default 1) + -d016offset int + number of pixels to shift with d016 when using interlace (default 1) + -display + include displayer + -force-border-color int + force border color (default -1) + -force-pack-empty + optimize packing empty chars (only for sccharset) + -fpe + force-pack-empty + -frame-delay int + frames to wait before displaying next animation frame (default 6) + -h help + -help + help + -i interlace + -interlace + when you supply 2 frames, specify -interlace to treat the images as such + -m string + mode + -memprofile file + write memory profile to file (only in -parallel mode) + -mode string + force graphics mode to koala, hires, mixedcharset, sccharset, mccharset (4col), scsprites or mcsprites + -na + no-anim + -nbc + no-bitpair-counters + -nc + no-crunch + -nf + no-fade + -ng + no-guess + -nl + no-loop + -no-anim + disable charset animations and store frames as separate screens + -no-bitpair-counters + do not use c64color bitpar counters optimization + -no-crunch + do not TSCrunch displayer + -no-fade + do not use fade in/out and free up a lot of memory + -no-guess + do not guess preferred bitpair-colors + -no-loop + play animations only once + -no-pack + do not pack chars (only for sc/mc charset) + -no-pack-empty + do not optimize packing empty chars (only for mc/mixed/ecm charset) + -no-prev-char-colors + do not look at the previous char's bitpair-colors, in some cases this optimization causes worse pack results + -np + no-pack + -npcc + no-prev-char-colors + -npe + no-pack-empty + -o string + out + -out string + specify outfile.prg, by default it changes extension to .prg + -p parallel + -parallel + run number of workers in parallel for fast conversion, treat each image as a standalone, not to be used for animations, unless an anim.csv is used + -q quiet + -quiet + quiet, only display errors + -sid string + include .sid in displayer (see -help for free memory locations) + -sym + symbols + -symbols + export symbols to .sym + -targetdir string + specify targetdir + -td string + targetdir + -trd + has side effect of enforcing screenram bitpair colors in level area + -v verbose + -verbose + verbose output + -vv + very verbose, show memory usage map in most cases and implies -verbose + -w int + workers (default 12) + -wait-seconds int + seconds to wait before animation starts + -workers int + number of concurrent workers in -parallel or -brute-force mode (default 12) +``` + diff --git a/loader/samples/minexample/png2prg/title_ifli.png b/loader/samples/minexample/png2prg/title_ifli.png new file mode 100644 index 0000000..7b305a5 Binary files /dev/null and b/loader/samples/minexample/png2prg/title_ifli.png differ diff --git a/loader/samples/minexample/png2prg/title_ifli_i1.png b/loader/samples/minexample/png2prg/title_ifli_i1.png new file mode 100644 index 0000000..5b962ec Binary files /dev/null and b/loader/samples/minexample/png2prg/title_ifli_i1.png differ diff --git a/loader/samples/minexample/png2prg/title_ifli_i1_2x.png b/loader/samples/minexample/png2prg/title_ifli_i1_2x.png new file mode 100644 index 0000000..a16db8e Binary files /dev/null and b/loader/samples/minexample/png2prg/title_ifli_i1_2x.png differ diff --git a/loader/samples/minexample/png2prg/title_ifli_i2.png b/loader/samples/minexample/png2prg/title_ifli_i2.png new file mode 100644 index 0000000..2227922 Binary files /dev/null and b/loader/samples/minexample/png2prg/title_ifli_i2.png differ diff --git a/loader/samples/minexample/png2prg/title_ifli_i2_2x.png b/loader/samples/minexample/png2prg/title_ifli_i2_2x.png new file mode 100644 index 0000000..258ba7a Binary files /dev/null and b/loader/samples/minexample/png2prg/title_ifli_i2_2x.png differ diff --git a/loader/samples/minexample/png2prg/ys2.csv b/loader/samples/minexample/png2prg/ys2.csv new file mode 100644 index 0000000..2b88a26 --- /dev/null +++ b/loader/samples/minexample/png2prg/ys2.csv @@ -0,0 +1,29 @@ + +5,ys2/01_badguyz/koala/png/01.png +5,ys2/01_badguyz/koala/png/02.png +5,ys2/01_badguyz/koala/png/03.png +5,ys2/01_badguyz/koala/png/04.png +5,ys2/01_badguyz/koala/png/05.png +50,ys2/01_badguyz/koala/png/06.png +15,ys2/01_badguyz/koala/png/07.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/11.png +100,ys2/01_badguyz/koala/png/12.png +15,ys2/01_badguyz/koala/png/10.png +15,ys2/01_badguyz/koala/png/09.png +15,ys2/01_badguyz/koala/png/08.png +15,ys2/01_badguyz/koala/png/07.png +100,ys2/01_badguyz/koala/png/06.png +5,ys2/01_badguyz/koala/png/13.png +5,ys2/01_badguyz/koala/png/14.png +5,ys2/01_badguyz/koala/png/15.png +5,ys2/01_badguyz/koala/png/16.png +5,ys2/01_badguyz/koala/png/17.png +5,ys2/01_badguyz/koala/png/18.png +5,ys2/01_badguyz/koala/png/19.png +100,ys2/01_badguyz/koala/png/00.png + + + diff --git a/loader/samples/minexample/png2prg/ys2.prg b/loader/samples/minexample/png2prg/ys2.prg new file mode 100644 index 0000000..8d72c17 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2.prg differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/00.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/00.bmp new file mode 100644 index 0000000..82de4ee Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/00.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/01.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/01.bmp new file mode 100644 index 0000000..65e7f04 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/02.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/02.bmp new file mode 100644 index 0000000..1d48923 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/03.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/03.bmp new file mode 100644 index 0000000..41e88eb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/04.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/04.bmp new file mode 100644 index 0000000..98c4b2d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/05.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/05.bmp new file mode 100644 index 0000000..35fbea2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/06.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/06.bmp new file mode 100644 index 0000000..317c234 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/07.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/07.bmp new file mode 100644 index 0000000..b6ba708 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/08.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/08.bmp new file mode 100644 index 0000000..aee0f7d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/09.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/09.bmp new file mode 100644 index 0000000..e9fa5de Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/10.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/10.bmp new file mode 100644 index 0000000..902fcb8 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/11.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/11.bmp new file mode 100644 index 0000000..0846091 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/12.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/12.bmp new file mode 100644 index 0000000..b15dc55 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/13.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/13.bmp new file mode 100644 index 0000000..49ca24e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/14.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/14.bmp new file mode 100644 index 0000000..a8a6e42 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/15.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/15.bmp new file mode 100644 index 0000000..91edd40 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/16.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/16.bmp new file mode 100644 index 0000000..c33752c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/17.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/17.bmp new file mode 100644 index 0000000..9e195b6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/18.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/18.bmp new file mode 100644 index 0000000..1d3bf2c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/19.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/19.bmp new file mode 100644 index 0000000..39509d6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/00.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/00.bmp new file mode 100644 index 0000000..958551c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/00.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/01.bmp new file mode 100644 index 0000000..df502bb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/02.bmp new file mode 100644 index 0000000..9def36a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/03.bmp new file mode 100644 index 0000000..9c2509d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/04.bmp new file mode 100644 index 0000000..2f7961e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/05.bmp new file mode 100644 index 0000000..59857ec Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/06.bmp new file mode 100644 index 0000000..ad1acd2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/07.bmp new file mode 100644 index 0000000..12b8b14 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/08.bmp new file mode 100644 index 0000000..6722858 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/09.bmp new file mode 100644 index 0000000..bcf8dc2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/10.bmp new file mode 100644 index 0000000..c05de60 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/11.bmp new file mode 100644 index 0000000..52d4d08 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/12.bmp new file mode 100644 index 0000000..e18b0e4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/13.bmp new file mode 100644 index 0000000..26c797c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/14.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/14.bmp new file mode 100644 index 0000000..0d0a1cd Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/15.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/15.bmp new file mode 100644 index 0000000..d8fd26c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/16.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/16.bmp new file mode 100644 index 0000000..cf7bab3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/17.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/17.bmp new file mode 100644 index 0000000..3b4941e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/18.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/18.bmp new file mode 100644 index 0000000..523831a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/19.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/19.bmp new file mode 100644 index 0000000..84daa34 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/00.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/00.bmp new file mode 100644 index 0000000..958551c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/00.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/01.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/01.bmp new file mode 100644 index 0000000..df502bb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/02.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/02.bmp new file mode 100644 index 0000000..9def36a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/03.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/03.bmp new file mode 100644 index 0000000..9c2509d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/04.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/04.bmp new file mode 100644 index 0000000..2f7961e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/05.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/05.bmp new file mode 100644 index 0000000..59857ec Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/06.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/06.bmp new file mode 100644 index 0000000..ad1acd2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/07.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/07.bmp new file mode 100644 index 0000000..12b8b14 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/08.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/08.bmp new file mode 100644 index 0000000..6722858 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/09.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/09.bmp new file mode 100644 index 0000000..bcf8dc2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/10.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/10.bmp new file mode 100644 index 0000000..c05de60 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/11.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/11.bmp new file mode 100644 index 0000000..52d4d08 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/12.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/12.bmp new file mode 100644 index 0000000..e18b0e4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/13.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/13.bmp new file mode 100644 index 0000000..26c797c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/14.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/14.bmp new file mode 100644 index 0000000..0d0a1cd Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/15.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/15.bmp new file mode 100644 index 0000000..d8fd26c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/16.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/16.bmp new file mode 100644 index 0000000..cf7bab3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/17.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/17.bmp new file mode 100644 index 0000000..3b4941e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/18.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/18.bmp new file mode 100644 index 0000000..523831a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/19.bmp b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/19.bmp new file mode 100644 index 0000000..84daa34 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/bak/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/00.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/00.png new file mode 100644 index 0000000..1de54c8 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/00.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/01.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/01.png new file mode 100644 index 0000000..9e48d61 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/02.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/02.png new file mode 100644 index 0000000..e59d44a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/03.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/03.png new file mode 100644 index 0000000..b6dad32 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/04.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/04.png new file mode 100644 index 0000000..6053816 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/05.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/05.png new file mode 100644 index 0000000..1d22c6b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/06.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/06.png new file mode 100644 index 0000000..6c27038 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/07.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/07.png new file mode 100644 index 0000000..e449883 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/08.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/08.png new file mode 100644 index 0000000..8d03c5d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/09.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/09.png new file mode 100644 index 0000000..ccf5c76 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/10.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/10.png new file mode 100644 index 0000000..198376d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/11.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/11.png new file mode 100644 index 0000000..a563e5f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/12.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/12.png new file mode 100644 index 0000000..3e375e9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/13.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/13.png new file mode 100644 index 0000000..87a6565 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/14.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/14.png new file mode 100644 index 0000000..70c2277 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/15.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/15.png new file mode 100644 index 0000000..d75d087 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/16.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/16.png new file mode 100644 index 0000000..5c65324 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/16.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/17.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/17.png new file mode 100644 index 0000000..90786ea Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/17.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/18.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/18.png new file mode 100644 index 0000000..35af5ee Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/18.png differ diff --git a/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/19.png b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/19.png new file mode 100644 index 0000000..afc11e2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/01_badguyz/koala/png/19.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title.bmp b/loader/samples/minexample/png2prg/ys2/02_title/title.bmp new file mode 100644 index 0000000..432e617 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title.png b/loader/samples/minexample/png2prg/ys2/02_title/title.png new file mode 100644 index 0000000..d91f4c3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title.prg b/loader/samples/minexample/png2prg/ys2/02_title/title.prg new file mode 100644 index 0000000..a939ac6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title.prg differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_clashstate.png b/loader/samples/minexample/png2prg/ys2/02_title/title_clashstate.png new file mode 100644 index 0000000..9d0a769 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_clashstate.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.bmp b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.bmp new file mode 100644 index 0000000..2fd7620 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.png b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.png new file mode 100644 index 0000000..7b305a5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1.png b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1.png new file mode 100644 index 0000000..5b962ec Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.bmp b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.bmp new file mode 100644 index 0000000..9e0417e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.png b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.png new file mode 100644 index 0000000..a16db8e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i1_2x.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2.png b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2.png new file mode 100644 index 0000000..2227922 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2.png differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.bmp b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.bmp new file mode 100644 index 0000000..c5500f6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.png b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.png new file mode 100644 index 0000000..258ba7a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/02_title/title_ifli_i2_2x.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/01.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/01.bmp new file mode 100644 index 0000000..910862d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/02.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/02.bmp new file mode 100644 index 0000000..d89fcda Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/03.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/03.bmp new file mode 100644 index 0000000..0d27ad9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/04.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/04.bmp new file mode 100644 index 0000000..b8f0c83 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/05.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/05.bmp new file mode 100644 index 0000000..2c3472e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/06.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/06.bmp new file mode 100644 index 0000000..e38585c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/07.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/07.bmp new file mode 100644 index 0000000..d764d7e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/08.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/08.bmp new file mode 100644 index 0000000..ab9b2c6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/09.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/09.bmp new file mode 100644 index 0000000..77e4bd2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/10.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/10.bmp new file mode 100644 index 0000000..5595e55 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/11.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/11.bmp new file mode 100644 index 0000000..418970b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/12.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/12.bmp new file mode 100644 index 0000000..cbf2542 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/13.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/13.bmp new file mode 100644 index 0000000..4d8464b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/14.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/14.bmp new file mode 100644 index 0000000..489b4f9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/15.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/15.bmp new file mode 100644 index 0000000..e5efed4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/01.bmp new file mode 100644 index 0000000..447d7e1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/02.bmp new file mode 100644 index 0000000..78226e0 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/03.bmp new file mode 100644 index 0000000..ad55e85 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/04.bmp new file mode 100644 index 0000000..e9260b9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/05.bmp new file mode 100644 index 0000000..a578207 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/06.bmp new file mode 100644 index 0000000..fc6e91a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/07.bmp new file mode 100644 index 0000000..ad8553d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/08.bmp new file mode 100644 index 0000000..63011f1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/09.bmp new file mode 100644 index 0000000..10779c4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/10.bmp new file mode 100644 index 0000000..f9a36a8 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/11.bmp new file mode 100644 index 0000000..dff5af9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/12 - Copy.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/12 - Copy.bmp new file mode 100644 index 0000000..0ad3ab7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/12 - Copy.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/12.bmp new file mode 100644 index 0000000..e983609 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/13.bmp new file mode 100644 index 0000000..4ae4d2d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/14.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/14.bmp new file mode 100644 index 0000000..6f7d272 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/15.bmp b/loader/samples/minexample/png2prg/ys2/03_tower/koala/15.bmp new file mode 100644 index 0000000..2376ac2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/01.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/01.png new file mode 100644 index 0000000..d09cf1a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/01.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/02.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/02.png new file mode 100644 index 0000000..04f03b6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/02.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/03.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/03.png new file mode 100644 index 0000000..0a1ca49 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/03.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/04.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/04.png new file mode 100644 index 0000000..4639624 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/04.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/05.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/05.png new file mode 100644 index 0000000..160568f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/05.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/06.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/06.png new file mode 100644 index 0000000..f9dd91e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/06.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/07.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/07.png new file mode 100644 index 0000000..339137a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/07.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/08.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/08.png new file mode 100644 index 0000000..c7908f5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/08.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/09.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/09.png new file mode 100644 index 0000000..1b2543c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/09.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/10.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/10.png new file mode 100644 index 0000000..d09f91c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/10.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/11.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/11.png new file mode 100644 index 0000000..c081c1a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/11.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/12.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/12.png new file mode 100644 index 0000000..b3706bf Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/12.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/13.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/13.png new file mode 100644 index 0000000..4f77663 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/13.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/14.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/14.png new file mode 100644 index 0000000..bec2e66 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/14.png differ diff --git a/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/15.png b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/15.png new file mode 100644 index 0000000..323ddad Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/03_tower/koala/png/15.png differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/01.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/01.bmp new file mode 100644 index 0000000..bd903d1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/02.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/02.bmp new file mode 100644 index 0000000..576de35 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/03.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/03.bmp new file mode 100644 index 0000000..5b9c0cb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/04.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/04.bmp new file mode 100644 index 0000000..abb6e44 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/05.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/05.bmp new file mode 100644 index 0000000..26fc46c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/06.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/06.bmp new file mode 100644 index 0000000..65cabe0 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/07.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/07.bmp new file mode 100644 index 0000000..75213b5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/08.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/08.bmp new file mode 100644 index 0000000..9ca39f2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/09.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/09.bmp new file mode 100644 index 0000000..74e3f06 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/10.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/10.bmp new file mode 100644 index 0000000..a1fce0d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/11.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/11.bmp new file mode 100644 index 0000000..f175bca Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/12.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/12.bmp new file mode 100644 index 0000000..b014ac8 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/13.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/13.bmp new file mode 100644 index 0000000..c62226b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/01.bmp new file mode 100644 index 0000000..2eaccf7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/02.bmp new file mode 100644 index 0000000..2be5df2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/03.bmp new file mode 100644 index 0000000..f018218 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/04.bmp new file mode 100644 index 0000000..34fb211 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/05.bmp new file mode 100644 index 0000000..cba40de Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/06.bmp new file mode 100644 index 0000000..4eaa1d3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/07.bmp new file mode 100644 index 0000000..8ee9ea7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/08.bmp new file mode 100644 index 0000000..c673e80 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/09.bmp new file mode 100644 index 0000000..2aae831 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/10.bmp new file mode 100644 index 0000000..fb803c5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/11.bmp new file mode 100644 index 0000000..15ddc61 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/12.bmp new file mode 100644 index 0000000..0c2034d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/13.bmp new file mode 100644 index 0000000..15e3ca3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/04_tower_beam/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/01.bmp b/loader/samples/minexample/png2prg/ys2/05_field/01.bmp new file mode 100644 index 0000000..049d50f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/02.bmp b/loader/samples/minexample/png2prg/ys2/05_field/02.bmp new file mode 100644 index 0000000..b77b8ef Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/03.bmp b/loader/samples/minexample/png2prg/ys2/05_field/03.bmp new file mode 100644 index 0000000..3c05604 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/04.bmp b/loader/samples/minexample/png2prg/ys2/05_field/04.bmp new file mode 100644 index 0000000..f666f07 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/05.bmp b/loader/samples/minexample/png2prg/ys2/05_field/05.bmp new file mode 100644 index 0000000..d5bd4c1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/06.bmp b/loader/samples/minexample/png2prg/ys2/05_field/06.bmp new file mode 100644 index 0000000..a608b27 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/07.bmp b/loader/samples/minexample/png2prg/ys2/05_field/07.bmp new file mode 100644 index 0000000..64fc1cb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/08.bmp b/loader/samples/minexample/png2prg/ys2/05_field/08.bmp new file mode 100644 index 0000000..9bb93ef Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/09.bmp b/loader/samples/minexample/png2prg/ys2/05_field/09.bmp new file mode 100644 index 0000000..7b95470 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/10.bmp b/loader/samples/minexample/png2prg/ys2/05_field/10.bmp new file mode 100644 index 0000000..63c675a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/11.bmp b/loader/samples/minexample/png2prg/ys2/05_field/11.bmp new file mode 100644 index 0000000..27effb5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/12.bmp b/loader/samples/minexample/png2prg/ys2/05_field/12.bmp new file mode 100644 index 0000000..35d839c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/13.bmp b/loader/samples/minexample/png2prg/ys2/05_field/13.bmp new file mode 100644 index 0000000..52b4552 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/14.bmp b/loader/samples/minexample/png2prg/ys2/05_field/14.bmp new file mode 100644 index 0000000..a5d82dc Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/15.bmp b/loader/samples/minexample/png2prg/ys2/05_field/15.bmp new file mode 100644 index 0000000..8818dd3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/16.bmp b/loader/samples/minexample/png2prg/ys2/05_field/16.bmp new file mode 100644 index 0000000..3e76fd2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/17.bmp b/loader/samples/minexample/png2prg/ys2/05_field/17.bmp new file mode 100644 index 0000000..ac85d97 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/18.bmp b/loader/samples/minexample/png2prg/ys2/05_field/18.bmp new file mode 100644 index 0000000..b960e4d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/19.bmp b/loader/samples/minexample/png2prg/ys2/05_field/19.bmp new file mode 100644 index 0000000..fc9d949 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/20.bmp b/loader/samples/minexample/png2prg/ys2/05_field/20.bmp new file mode 100644 index 0000000..de70720 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/20.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/21.bmp b/loader/samples/minexample/png2prg/ys2/05_field/21.bmp new file mode 100644 index 0000000..b800127 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/21.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/22.bmp b/loader/samples/minexample/png2prg/ys2/05_field/22.bmp new file mode 100644 index 0000000..c18eb6d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/22.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/23.bmp b/loader/samples/minexample/png2prg/ys2/05_field/23.bmp new file mode 100644 index 0000000..5dd70be Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/23.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/24.bmp b/loader/samples/minexample/png2prg/ys2/05_field/24.bmp new file mode 100644 index 0000000..59654e4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/24.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/25.bmp b/loader/samples/minexample/png2prg/ys2/05_field/25.bmp new file mode 100644 index 0000000..2dd73f6 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/25.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/26.bmp b/loader/samples/minexample/png2prg/ys2/05_field/26.bmp new file mode 100644 index 0000000..91382bd Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/26.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/27.bmp b/loader/samples/minexample/png2prg/ys2/05_field/27.bmp new file mode 100644 index 0000000..93d134f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/27.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/28.bmp b/loader/samples/minexample/png2prg/ys2/05_field/28.bmp new file mode 100644 index 0000000..4e23881 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/28.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/29.bmp b/loader/samples/minexample/png2prg/ys2/05_field/29.bmp new file mode 100644 index 0000000..bf18642 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/29.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/30.bmp b/loader/samples/minexample/png2prg/ys2/05_field/30.bmp new file mode 100644 index 0000000..3f96d1e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/30.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/01.bmp new file mode 100644 index 0000000..56dd0e5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/02.bmp new file mode 100644 index 0000000..e30e644 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/03.bmp new file mode 100644 index 0000000..70da67b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/04.bmp new file mode 100644 index 0000000..bb59216 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/05.bmp new file mode 100644 index 0000000..4bafefb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/06.bmp new file mode 100644 index 0000000..6dca3f1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/07.bmp new file mode 100644 index 0000000..739c891 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/08.bmp new file mode 100644 index 0000000..566467f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/09.bmp new file mode 100644 index 0000000..22b34a9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/10.bmp new file mode 100644 index 0000000..b7ae6b2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/11.bmp new file mode 100644 index 0000000..4fe700c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/12.bmp new file mode 100644 index 0000000..3de9b6f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/13.bmp new file mode 100644 index 0000000..59122ff Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/14.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/14.bmp new file mode 100644 index 0000000..0002788 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/15.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/15.bmp new file mode 100644 index 0000000..741a63f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/16.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/16.bmp new file mode 100644 index 0000000..bb9e5d0 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/17.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/17.bmp new file mode 100644 index 0000000..49b6839 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/18.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/18.bmp new file mode 100644 index 0000000..5057e17 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/19.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/19.bmp new file mode 100644 index 0000000..4505d1d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/20.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/20.bmp new file mode 100644 index 0000000..c86237d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/20.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/21.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/21.bmp new file mode 100644 index 0000000..a589521 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/21.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/22.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/22.bmp new file mode 100644 index 0000000..1eade56 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/22.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/23.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/23.bmp new file mode 100644 index 0000000..5d722f3 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/23.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/24.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/24.bmp new file mode 100644 index 0000000..17caa52 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/24.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/25.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/25.bmp new file mode 100644 index 0000000..2000a55 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/25.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/26.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/26.bmp new file mode 100644 index 0000000..a8180ed Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/26.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/27.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/27.bmp new file mode 100644 index 0000000..030373f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/27.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/28.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/28.bmp new file mode 100644 index 0000000..a252b31 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/28.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/29.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/29.bmp new file mode 100644 index 0000000..39ca8d2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/29.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/05_field/koala/30.bmp b/loader/samples/minexample/png2prg/ys2/05_field/koala/30.bmp new file mode 100644 index 0000000..dc6310d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/05_field/koala/30.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/01.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/01.bmp new file mode 100644 index 0000000..4429d28 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/02.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/02.bmp new file mode 100644 index 0000000..a649c0b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/03.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/03.bmp new file mode 100644 index 0000000..b36f84d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/04.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/04.bmp new file mode 100644 index 0000000..9243cea Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/05.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/05.bmp new file mode 100644 index 0000000..6bf9601 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/06.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/06.bmp new file mode 100644 index 0000000..6633215 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/07.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/07.bmp new file mode 100644 index 0000000..2d1e9f9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/08.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/08.bmp new file mode 100644 index 0000000..7d68d6f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/09.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/09.bmp new file mode 100644 index 0000000..2caaf87 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/10.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/10.bmp new file mode 100644 index 0000000..8f1f08b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/11.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/11.bmp new file mode 100644 index 0000000..9c48e59 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/12.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/12.bmp new file mode 100644 index 0000000..dc48b1d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/13.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/13.bmp new file mode 100644 index 0000000..919e9aa Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/14.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/14.bmp new file mode 100644 index 0000000..722131d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/15.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/15.bmp new file mode 100644 index 0000000..f03657e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/16.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/16.bmp new file mode 100644 index 0000000..cf4aaec Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/17.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/17.bmp new file mode 100644 index 0000000..4e11d08 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/18.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/18.bmp new file mode 100644 index 0000000..a768632 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/19.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/19.bmp new file mode 100644 index 0000000..2ed89ff Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/01.bmp new file mode 100644 index 0000000..bb3e6dd Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/02.bmp new file mode 100644 index 0000000..007250b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/03.bmp new file mode 100644 index 0000000..bf14966 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/04.bmp new file mode 100644 index 0000000..47254fe Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/05.bmp new file mode 100644 index 0000000..f587e57 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/06.bmp new file mode 100644 index 0000000..cfc3629 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/07.bmp new file mode 100644 index 0000000..6fcee4e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/08.bmp new file mode 100644 index 0000000..543c3f1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/09.bmp new file mode 100644 index 0000000..37ad2ca Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/10.bmp new file mode 100644 index 0000000..94eab40 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/11.bmp new file mode 100644 index 0000000..f3e2caa Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/12.bmp new file mode 100644 index 0000000..10c74dc Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/13.bmp new file mode 100644 index 0000000..b9bf5a5 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/14.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/14.bmp new file mode 100644 index 0000000..15b1a8f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/15.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/15.bmp new file mode 100644 index 0000000..0e2e48f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/16.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/16.bmp new file mode 100644 index 0000000..b145d2b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/17.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/17.bmp new file mode 100644 index 0000000..0a1a0c9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/17.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/18.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/18.bmp new file mode 100644 index 0000000..b458261 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/18.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/19.bmp b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/19.bmp new file mode 100644 index 0000000..28a2df7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/06_falling_star/koala/19.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia.csv b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia.csv new file mode 100644 index 0000000..d366d4a --- /dev/null +++ b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia.csv @@ -0,0 +1,9 @@ +50,lilia/lilia1.png +4,lilia/lilia2.png +4,lilia/lilia3.png +4,lilia/lilia4.png +4,lilia/lilia5.png +4,lilia/lilia6.png +4,lilia/lilia7.png +4,lilia/lilia8.png +250,lilia/lilia9.png diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia1.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia1.png new file mode 100644 index 0000000..e63a1bd Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia1.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia2.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia2.png new file mode 100644 index 0000000..013a981 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia2.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia3.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia3.png new file mode 100644 index 0000000..5001341 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia3.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia4.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia4.png new file mode 100644 index 0000000..56ee1b9 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia4.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia5.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia5.png new file mode 100644 index 0000000..1b9662a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia5.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia6.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia6.png new file mode 100644 index 0000000..8d55638 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia6.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia7.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia7.png new file mode 100644 index 0000000..06db685 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia7.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia8.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia8.png new file mode 100644 index 0000000..9f6fa29 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia8.png differ diff --git a/loader/samples/minexample/png2prg/ys2/07_lilia/lilia9.png b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia9.png new file mode 100644 index 0000000..4c9e235 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/07_lilia/lilia9.png differ diff --git a/loader/samples/minexample/png2prg/ys2/08_lilia_hero/01.bmp b/loader/samples/minexample/png2prg/ys2/08_lilia_hero/01.bmp new file mode 100644 index 0000000..8a4df67 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/08_lilia_hero/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/08_lilia_hero/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/08_lilia_hero/koala/01.bmp new file mode 100644 index 0000000..d7326a7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/08_lilia_hero/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/01.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/01.bmp new file mode 100644 index 0000000..3f130de Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/02.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/02.bmp new file mode 100644 index 0000000..738cbb1 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/03.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/03.bmp new file mode 100644 index 0000000..763dcce Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/04.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/04.bmp new file mode 100644 index 0000000..68d679b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/05.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/05.bmp new file mode 100644 index 0000000..b5f7d86 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/06.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/06.bmp new file mode 100644 index 0000000..544db82 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/07.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/07.bmp new file mode 100644 index 0000000..eb6aa2b Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/08.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/08.bmp new file mode 100644 index 0000000..1b87a02 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/09.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/09.bmp new file mode 100644 index 0000000..2c23cd4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/10.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/10.bmp new file mode 100644 index 0000000..06b670e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/11.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/11.bmp new file mode 100644 index 0000000..8be2b0d Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/12.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/12.bmp new file mode 100644 index 0000000..208ffec Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/13.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/13.bmp new file mode 100644 index 0000000..f9822f4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/14.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/14.bmp new file mode 100644 index 0000000..999e70a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/15.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/15.bmp new file mode 100644 index 0000000..4acbbb8 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/16.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/16.bmp new file mode 100644 index 0000000..a301bc7 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/16.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/happee.png b/loader/samples/minexample/png2prg/ys2/11_lilia_final/happee.png new file mode 100644 index 0000000..b6939fb Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/happee.png differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/01.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/01.bmp new file mode 100644 index 0000000..8b0746f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/01.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/02.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/02.bmp new file mode 100644 index 0000000..67fda35 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/02.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/03.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/03.bmp new file mode 100644 index 0000000..4b22f0f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/03.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/04.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/04.bmp new file mode 100644 index 0000000..3cd9c43 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/04.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/05.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/05.bmp new file mode 100644 index 0000000..6edaed4 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/05.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/06.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/06.bmp new file mode 100644 index 0000000..b9ff83c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/06.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/07.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/07.bmp new file mode 100644 index 0000000..f59b472 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/07.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/08.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/08.bmp new file mode 100644 index 0000000..dfb7ac2 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/08.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/09.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/09.bmp new file mode 100644 index 0000000..9730328 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/09.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/10.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/10.bmp new file mode 100644 index 0000000..57f0c1e Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/10.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/11.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/11.bmp new file mode 100644 index 0000000..b546e8a Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/11.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/12.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/12.bmp new file mode 100644 index 0000000..7358307 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/12.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/13.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/13.bmp new file mode 100644 index 0000000..a6ad68c Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/13.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/14.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/14.bmp new file mode 100644 index 0000000..a2e293f Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/14.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/15.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/15.bmp new file mode 100644 index 0000000..560cb07 Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/15.bmp differ diff --git a/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/16.bmp b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/16.bmp new file mode 100644 index 0000000..ff406ca Binary files /dev/null and b/loader/samples/minexample/png2prg/ys2/11_lilia_final/koala/16.bmp differ diff --git a/loader/samples/minexample/sfx.asm b/loader/samples/minexample/sfx.asm new file mode 100644 index 0000000..ca13030 --- /dev/null +++ b/loader/samples/minexample/sfx.asm @@ -0,0 +1,87 @@ +sfx_init = sfx +sfx_play = sfx+3 +.proc sfx +init: + jmp real_init +play: + lda sfx_done + bne @skip_update + + dec tick + lda tick + bne @skip_update + +@update_loop: + jsr load_sfx_byte + cmp #$80 + bcs @update_loop_end + tay + jsr load_sfx_byte + sta $d400, y + jmp @update_loop +@update_loop_end: + cmp #$ff + bne :+++ + ldx sfx_num + bne :+ + lda #1 + jmp real_init +: + cpx #2 + bcs :+ + lda #2 + jmp real_init +: + sta sfx_done + rts +: + and #$7f + sta tick +@skip_update: + rts + +real_init: + sta sfx_num + asl + tax + lda sfx_list, x + sta sfx_byte_SMC+1 + lda sfx_list+1, x + sta sfx_byte_SMC+2 + + lda sfx_num + cmp #2 + beq :++ + ldx #$18 + lda #0 +: + sta $d400, x + dex + bpl :- +: + lda #0 + sta sfx_index + sta sfx_done + lda #1 + sta tick + rts + +load_sfx_byte: + ldx sfx_index +sfx_byte_SMC: + lda $1000, x + inx + stx sfx_index + rts + +sfx_index: .res 1 +tick: .res 1 +sfx_done: .res 1 +sfx_num: .res 1 +sfx_list: + .word sfx0, sfx1, sfx1+$29 +sfx0: + .incbin "sfx_badguy.bin" +sfx1: + .incbin "sfx_badguy_amb.bin" +.endproc \ No newline at end of file diff --git a/loader/samples/minexample/sfx_badguy.bin b/loader/samples/minexample/sfx_badguy.bin new file mode 100644 index 0000000..4dfe30b Binary files /dev/null and b/loader/samples/minexample/sfx_badguy.bin differ diff --git a/loader/samples/minexample/sfx_badguy_amb.bin b/loader/samples/minexample/sfx_badguy_amb.bin new file mode 100644 index 0000000..81f110b --- /dev/null +++ b/loader/samples/minexample/sfx_badguy_amb.bin @@ -0,0 +1,4 @@ +ÿ… ‚ €  +A„„„„„„„ „ +„ „ „ „„„„„„„„„„„„„„„„„„„ „!„"„#„$„%$„#„"„!„ „„„„„„„„„„„„„„„„„„„ „ „ „ +„ „„„„„„„ÿƒÿ \ No newline at end of file diff --git a/loader/samples/minexample/sid.bin b/loader/samples/minexample/sid.bin new file mode 100644 index 0000000..74d7539 Binary files /dev/null and b/loader/samples/minexample/sid.bin differ diff --git a/loader/samples/minexample/ys2_new.bin b/loader/samples/minexample/ys2_new.bin new file mode 100644 index 0000000..35c2508 Binary files /dev/null and b/loader/samples/minexample/ys2_new.bin differ diff --git a/loader/samples/minexample/ys2_sid.sid b/loader/samples/minexample/ys2_sid.sid new file mode 100644 index 0000000..ef7c8a6 Binary files /dev/null and b/loader/samples/minexample/ys2_sid.sid differ diff --git a/loader/samples/minexample/ys2sid.bin b/loader/samples/minexample/ys2sid.bin new file mode 100644 index 0000000..fd26ed3 Binary files /dev/null and b/loader/samples/minexample/ys2sid.bin differ diff --git a/loader/samples/minexample/zx02 b/loader/samples/minexample/zx02 new file mode 160000 index 0000000..5f58196 --- /dev/null +++ b/loader/samples/minexample/zx02 @@ -0,0 +1 @@ +Subproject commit 5f58196c0c1ad24c8d01cfd7ded4930077a08a9c diff --git a/loader/samples/minexample/zx02.asm b/loader/samples/minexample/zx02.asm new file mode 100644 index 0000000..03afbbe --- /dev/null +++ b/loader/samples/minexample/zx02.asm @@ -0,0 +1,159 @@ +; De-compressor for ZX02 files +; ---------------------------- +; +; Decompress ZX02 data (6502 optimized format), optimized for speed and size +; 138 bytes code, 58.0 cycles/byte in test file. +; +; Compress with: +; zx02 input.bin output.zx0 +; +; (c) 2022 DMSC +; Code under MIT license, see LICENSE file. + + ; Initial values for offset, source, destination and bitr +;zx0_ini_block: +; .byte $00, $00 ; offset +;comp_data: +; .byte $0, $0 ; zx0_src +;out_addr: +; .byte $0, $0 ; zx0_dst +; .byte $80 ; bitr + +;-------------------------------------------------- +; Decompress ZX0 data (6502 optimized format) + + ; destination page in A +zx02: + sta ZX0_dst+1 + + ldy #$80 + sty bitr + + ldy #0 ; always on page boundary + sty ZX0_dst + + sty offset + sty offset+1 + + ; Y needs to be 0 here + + ; Get initialization block +; ldy #7 + +;copy_init: lda zx0_ini_block-1, y + ; sta offset-1, y + ; dey + ; bne copy_init + +; Decode literal: Ccopy next N bytes from compressed file +; Elias(length) byte[1] byte[2] ... byte[N] +decode_literal: + jsr get_elias + +cop0: lda (ZX0_src), y + inc ZX0_src + bne plus1 + inc ZX0_src+1 +plus1: sta (ZX0_dst),y +;nop +;nop + inc ZX0_dst + bne plus2 + inc ZX0_dst+1 +plus2: dex + bne cop0 + + asl bitr + bcs dzx0s_new_offset + +; Copy from last offset (repeat N bytes from last offset) +; Elias(length) + jsr get_elias +dzx0s_copy: + lda ZX0_dst + sbc offset ; C=0 from get_elias + sta pntr + lda ZX0_dst+1 + sbc offset+1 + sta pntr+1 + +cop1: + lda (pntr), y + inc pntr + bne plus3 + inc pntr+1 +plus3: sta (ZX0_dst),y +;nop +;nop + inc ZX0_dst + bne plus4 + inc ZX0_dst+1 +plus4: dex + bne cop1 + + asl bitr + bcc decode_literal + +; Copy from new offset (repeat N bytes from new offset) +; Elias(MSB(offset)) LSB(offset) Elias(length-1) +dzx0s_new_offset: + ; Read elias code for high part of offset + jsr get_elias + beq exit ; Read a 0, signals the end + ; Decrease and divide by 2 + dex + txa + lsr ; @ + sta offset+1 + + ; Get low part of offset, a literal 7 bits + lda (ZX0_src), y + inc ZX0_src + bne plus5 + inc ZX0_src+1 +plus5: + ; Divide by 2 + ror ; @ + sta offset + + ; And get the copy length. + ; Start elias reading with the bit already in carry: + ldx #1 + jsr elias_skip1 + + inx + bcc dzx0s_copy + +; Read an elias-gamma interlaced code. +; ------------------------------------ +get_elias: + ; Initialize return value to #1 + ldx #1 + bne elias_start + +elias_get: ; Read next data bit to result + asl bitr + rol ; @ + tax + +elias_start: + ; Get one bit + asl bitr + bne elias_skip1 + + ; Read new bit from stream + lda (ZX0_src), y + inc ZX0_src + bne plus6 + inc ZX0_src+1 +plus6: ;sec ; not needed, C=1 guaranteed from last bit + rol ;@ + sta bitr + +elias_skip1: + txa + bcs elias_get + ; Got ending bit, stop reading +exit: + rts + diff --git a/loader/samples/resources/a b/loader/samples/resources/a new file mode 100644 index 0000000..57b84e5 Binary files /dev/null and b/loader/samples/resources/a differ diff --git a/loader/samples/resources/b b/loader/samples/resources/b new file mode 100644 index 0000000..bf23458 Binary files /dev/null and b/loader/samples/resources/b differ diff --git a/loader/samples/resources/bootblock.bin b/loader/samples/resources/bootblock.bin new file mode 100644 index 0000000..53600c8 Binary files /dev/null and b/loader/samples/resources/bootblock.bin differ diff --git a/loader/samples/resources/c b/loader/samples/resources/c new file mode 100644 index 0000000..bfdce3b Binary files /dev/null and b/loader/samples/resources/c differ diff --git a/loader/samples/resources/d b/loader/samples/resources/d new file mode 100644 index 0000000..bee5840 Binary files /dev/null and b/loader/samples/resources/d differ diff --git a/loader/samples/resources/doom_c128.prg b/loader/samples/resources/doom_c128.prg new file mode 100644 index 0000000..199efef Binary files /dev/null and b/loader/samples/resources/doom_c128.prg differ diff --git a/loader/samples/resources/e b/loader/samples/resources/e new file mode 100644 index 0000000..02d2c18 Binary files /dev/null and b/loader/samples/resources/e differ diff --git a/loader/samples/resources/f b/loader/samples/resources/f new file mode 100644 index 0000000..ff9481a Binary files /dev/null and b/loader/samples/resources/f differ diff --git a/loader/samples/resources/g b/loader/samples/resources/g new file mode 100644 index 0000000..c69677b Binary files /dev/null and b/loader/samples/resources/g differ diff --git a/loader/samples/resources/h b/loader/samples/resources/h new file mode 100644 index 0000000..bec0869 Binary files /dev/null and b/loader/samples/resources/h differ diff --git a/loader/samples/resources/i b/loader/samples/resources/i new file mode 100644 index 0000000..b0b60d1 Binary files /dev/null and b/loader/samples/resources/i differ diff --git a/loader/samples/resources/j b/loader/samples/resources/j new file mode 100644 index 0000000..bdf733f Binary files /dev/null and b/loader/samples/resources/j differ diff --git a/loader/samples/resources/k b/loader/samples/resources/k new file mode 100644 index 0000000..ea6ea31 Binary files /dev/null and b/loader/samples/resources/k differ diff --git a/loader/samples/resources/l b/loader/samples/resources/l new file mode 100644 index 0000000..7b6114a Binary files /dev/null and b/loader/samples/resources/l differ diff --git a/loader/samples/resources/m b/loader/samples/resources/m new file mode 100644 index 0000000..94a78d6 Binary files /dev/null and b/loader/samples/resources/m differ diff --git a/loader/samples/resources/n b/loader/samples/resources/n new file mode 100644 index 0000000..5055476 Binary files /dev/null and b/loader/samples/resources/n differ diff --git a/loader/samples/resources/o b/loader/samples/resources/o new file mode 100644 index 0000000..2e38c07 Binary files /dev/null and b/loader/samples/resources/o differ diff --git a/loader/samples/resources/oxyron_oneder.prg b/loader/samples/resources/oxyron_oneder.prg new file mode 100644 index 0000000..b9ae55b Binary files /dev/null and b/loader/samples/resources/oxyron_oneder.prg differ diff --git a/loader/samples/resources/p b/loader/samples/resources/p new file mode 100644 index 0000000..840e076 Binary files /dev/null and b/loader/samples/resources/p differ diff --git a/loader/samples/resources/pic1.bin b/loader/samples/resources/pic1.bin new file mode 100644 index 0000000..5397173 Binary files /dev/null and b/loader/samples/resources/pic1.bin differ diff --git a/loader/samples/resources/pic2.bin b/loader/samples/resources/pic2.bin new file mode 100644 index 0000000..8228620 Binary files /dev/null and b/loader/samples/resources/pic2.bin differ diff --git a/loader/samples/resources/q b/loader/samples/resources/q new file mode 100644 index 0000000..59a4ec7 Binary files /dev/null and b/loader/samples/resources/q differ diff --git a/loader/samples/resources/r b/loader/samples/resources/r new file mode 100644 index 0000000..bba58d6 Binary files /dev/null and b/loader/samples/resources/r differ diff --git a/loader/samples/resources/skew1.prg b/loader/samples/resources/skew1.prg new file mode 100644 index 0000000..4f2e642 Binary files /dev/null and b/loader/samples/resources/skew1.prg differ diff --git a/loader/samples/resources/test b/loader/samples/resources/test new file mode 100644 index 0000000..3f92337 Binary files /dev/null and b/loader/samples/resources/test differ diff --git a/loader/samples/resources/threeve.prg b/loader/samples/resources/threeve.prg new file mode 100644 index 0000000..63feabb Binary files /dev/null and b/loader/samples/resources/threeve.prg differ diff --git a/loader/samples/resources/x b/loader/samples/resources/x new file mode 100644 index 0000000..0f5d90c Binary files /dev/null and b/loader/samples/resources/x differ diff --git a/loader/samples/resources/y b/loader/samples/resources/y new file mode 100644 index 0000000..232bdf7 Binary files /dev/null and b/loader/samples/resources/y differ diff --git a/loader/samples/save/Makefile b/loader/samples/save/Makefile new file mode 100644 index 0000000..91dffa5 --- /dev/null +++ b/loader/samples/save/Makefile @@ -0,0 +1,68 @@ + +NAME = save +_PLATFORM_ = c64 + +VICE = x64sc +EMU = $(VICE) -drive8type 1541 -drive9type 0 -autostart +EMU71 = $(VICE) -drive8type 1571 -drive9type 0 -autostart +EMU81 = $(VICE) -drive8type 1581 -drive9type 0 -autostart + +AS = cl65 +AS_FLAGS = -Wa -I../../build -Wa -I../../include -u __EXEHDR__ + +CC1541 = ../../tools/cc1541/cc1541 -v +CC1541_SOURCE = ../../tools/cc1541 + +ECHO = echo + +RM = rm -f +CP = cp +MV = mv +CAT = cat + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/save-$(_PLATFORM_).prg + +RESOURCESDIR = ../resources +TEST = $(RESOURCESDIR)/test.prg +SAVEFILE = $(INTERMDIR)/savefile.prg + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d64 +DISKIMAGE81 = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d81 + +$(DISKIMAGE) $(DISKIMAGE81): $(ASSEMBLE) $(SAVEFILE) $(CC1541) + $(RM) $@ + $(CC1541) -n "normal is boring" -i plush \ + -f $(NAME) -w $< \ + -f "savefile" -w $(SAVEFILE) \ + $@ + +$(ASSEMBLE): $(SOURCE) $(LOADER) + $(AS) $(AS_FLAGS) -C c64-asm.cfg -Wa -DPLATFORM=64 -o $@ $< + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) PLATFORM=$(_PLATFORM_) PROJECT=$(NAME) INSTALL=1000 RESIDENT=0F00 ZP=04 save + +$(SAVEFILE): + $(ECHO) "plush" > $@ + +run: $(DISKIMAGE) + $(EMU) $(realpath $^) + +run71: $(DISKIMAGE) + $(EMU71) $(realpath $^) + +run81: $(DISKIMAGE81) + $(EMU81) $(realpath $^) + +clean: + -$(RM) *.o $(SAVEFILE) $(ASSEMBLE) $(DISKIMAGE) $(BUILDDIR)/loadersymbols-c64.prg $(BUILDDIR)/install-c64.prg $(BUILDDIR)/loader-c64.prg $(BUILDDIR)/save-c64.prg + + +$(CC1541): $(CC1541_SOURCE)/cc1541.c + $(MAKE) -C $(CC1541_SOURCE) cc1541 diff --git a/loader/samples/save/loaderconfig.inc b/loader/samples/save/loaderconfig.inc new file mode 100644 index 0000000..33a8a3b --- /dev/null +++ b/loader/samples/save/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/save/main.asm b/loader/samples/save/main.asm new file mode 100644 index 0000000..51fdec7 --- /dev/null +++ b/loader/samples/save/main.asm @@ -0,0 +1,76 @@ +.feature c_comments + +.zeropage + +.segment "CODE" + +.org $080D +main: + sei + lda #$35 + sta $01 + + lda #127 + sta $dc0d + + and $d011 + sta $d011 + + lda $dc0d + lda $dd0d + + lda #irq + sta $ffff + + lda #$0b + sta $d011 + lda #$00 + sta $d012 + + lda #0 + sta $d01a + + lda #$63; <(985248/100) + sta $dc04 + lda #$26;>(985248/100) + sta $dc05 + + lda $dc0d + and #$81 + sta $dc0d + + lda #$40 + sta $dc0c + + lda #$81 + sta $dc0d + + lda #0 + jsr $1000 + cli + jmp * + +irq: + pha + txa + pha + tya + pha + + inc $d020 + jsr $1003 + dec $d020 + + pla + tay + pla + tax + pla + ;rti + jmp $dc0c + + +.res $1000-* +.incbin "ys2_sid.sid", $7e+($1000-$ff6) \ No newline at end of file diff --git a/loader/samples/save/save.s b/loader/samples/save/save.s new file mode 100644 index 0000000..4fa1ef5 --- /dev/null +++ b/loader/samples/save/save.s @@ -0,0 +1,42 @@ + +.include "loadersymbols-c64.inc" + + .org $080d + + jsr install + +loop: inc $d020 + + ldx #saveparams + jsr save + bcs error + + ldx #filename + jsr loadraw + bcs error + + lda $04fc + sta $0400 + bcc loop + +error: inc $d021 + jmp error + +saveparams: .word filename + .word $0400; from + .word $00fc; length in bytes + .word $0401; loadaddress + .word $8000; drive-code buffer, $0800 bytes to scratch + +filename: .byte "savefile", 0 + + .res loadraw - * +.incbin "../../build/loader-c64.prg", 2 + + .res save - * +.incbin "../../build/save-c64.prg", 2 + + .res install - * +.incbin "../../build/install-c64.prg", 2 diff --git a/loader/samples/save/ys2_sid.sid b/loader/samples/save/ys2_sid.sid new file mode 100644 index 0000000..ef7c8a6 Binary files /dev/null and b/loader/samples/save/ys2_sid.sid differ diff --git a/loader/samples/standalone/Linkfile-c128 b/loader/samples/standalone/Linkfile-c128 new file mode 100755 index 0000000..f38dae2 --- /dev/null +++ b/loader/samples/standalone/Linkfile-c128 @@ -0,0 +1,21 @@ +MEMORY +{ + LOADERZP: start = $f7, size = $07, type = rw; + LOWMEM: start = $0b00, size = $0100, type = rw; + RAM: start = $1c01, size = $a3ff, type = rw; + RESIDENT: start = $f900, size = $0600, type = rw; +} + +SEGMENTS +{ + CODE: load = RAM, type = ro; + + ROM_THUNKS: load = RAM, run = LOWMEM, define = yes; + + DISKIO_ZP: load = LOADERZP, type = zp; + DISKIO: load = RAM, run = RESIDENT, define = yes; + + RESIDENT_CODE: load = RAM, run = RESIDENT, define = yes; + + DISKIO_INSTALL: load = RAM; +} diff --git a/loader/samples/standalone/Linkfile-c16 b/loader/samples/standalone/Linkfile-c16 new file mode 100644 index 0000000..651975f --- /dev/null +++ b/loader/samples/standalone/Linkfile-c16 @@ -0,0 +1,20 @@ +MEMORY +{ + LOADERZP: start = $d8, size = $07, type = rw; + LOWMEM: start = $0334, size = $cc, type = rw; + RAM: start = $1c01, size = $63ff, type = rw; + RESIDENT: start = $f700, size = $0600, type = rw; +} + +SEGMENTS +{ + CODE: load = RAM, type = ro; + DISKIO_INSTALL: load = RAM; + + ROM_THUNKS: load = RAM, run = LOWMEM, define = yes; + + DISKIO_ZP: load = LOADERZP, type = zp; + DISKIO: load = RAM, run = RESIDENT, define = yes; + + RESIDENT_CODE: load = RAM, run = RESIDENT, define = yes; +} diff --git a/loader/samples/standalone/Linkfile-c64 b/loader/samples/standalone/Linkfile-c64 new file mode 100755 index 0000000..7f706f0 --- /dev/null +++ b/loader/samples/standalone/Linkfile-c64 @@ -0,0 +1,20 @@ +MEMORY +{ + LOADERZP: start = $f7, size = $07, type = rw; + LOWMEM: start = $0334, size = $cc, type = rw; + RAM: start = $1c01, size = $83ff, type = rw; + RESIDENT: start = $fa00, size = $05fa, type = rw; +} + +SEGMENTS +{ + CODE: load = RAM, type = ro; + DISKIO_INSTALL: load = RAM; + + ROM_THUNKS: load = RAM, run = LOWMEM, define = yes; + + DISKIO_ZP: load = LOADERZP, type = zp; + DISKIO: load = RAM, run = RESIDENT, define = yes; + + RESIDENT_CODE: load = RAM, run = RESIDENT, define = yes; +} diff --git a/loader/samples/standalone/Makefile b/loader/samples/standalone/Makefile new file mode 100755 index 0000000..1538838 --- /dev/null +++ b/loader/samples/standalone/Makefile @@ -0,0 +1,253 @@ + +ifeq ($(PLATFORM),) +_PLATFORM_ = c64 +else ifeq ($(PLATFORM),c116) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),plus4) +_PLATFORM_ = c16 +else +_PLATFORM_ = $(PLATFORM) +endif + +ifeq ($(NO_VICE),) +NO_VICE = 0 +endif + + +ifneq ($(_PLATFORM_),c64) +ifneq ($(_PLATFORM_),c128) +ifneq ($(_PLATFORM_),c16) +$(error invalid platform $(_PLATFORM_) specified) +endif +endif +endif + + +ARCH = $(shell uname | tr "[a-z]" "[A-Z]" | tr -c -d "[A-Z]") + +ifneq ($(findstring CYGWINNT,$(ARCH)),) + ifeq (CYGWINNT,$(ARCH)) +ARCH = WIN32 + else +ARCH = WIN64 + endif +endif +ifneq ($(findstring DARWIN,$(ARCH)),) +ARCH = MACOSX +endif + + +ifeq ($(_PLATFORM_),c16) + ifneq ($(NO_VICE),0) + ifneq ($(findstring WIN,$(ARCH)),) +USE_PLUS4EMU = 0 +USE_YAPE = 1 + else +USE_PLUS4EMU = 1 +USE_YAPE = 0 + endif + else +USE_PLUS4EMU = 0 +USE_YAPE = 0 + endif +else +USE_PLUS4EMU = 0 +USE_YAPE = 0 +endif + + +ifeq ($(_PLATFORM_),c16) + ifeq ($(ARCH),MACOSX) + # MacOSX, these programs must be installed as applications +VICE = xplus4 +PLUS4EMU = open /Applications/plus4emu.app --args + else +VICE = xplus4 +PLUS4EMU = plus4emu + ifeq ($(ARCH),WIN64) +YAPE = YapeWin64 + else +YAPE = Yape + endif + endif +else + ifeq ($(_PLATFORM_),c128) +VICE = x128 + else +VICE = x64 + endif +endif + + +ifneq ($(USE_PLUS4EMU),0) +EMU = $(PLUS4EMU) -disk +else +EMU = $(VICE) -drive8type 1541 -drive9type 0 -autostart +endif +EMU70 = $(VICE) -drive8type 1570 -drive9type 0 -autostart + + +ECHO = echo +PRINTF = printf + +AS = ca65 +LD = ld65 +PU = ../../tools/pucrunch/pucrunch +PU_SOURCE = ../../tools/pucrunch +CC1541 = ../../tools/cc1541 + +MKDIR = mkdir -p +RM = rm -f +ifeq ($(ARCH),MACOSX) +RMDIR = rmdir # XXX TODO xargs to remove .DS_Store +else +RMDIR = rmdir +endif +CAT = cat + + +.PHONY: default loader assemble link compress diskimage run clean distclean wipe +.PHONY: tellarch + + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/loader-$(_PLATFORM_).lib + +RESOURCESDIR = ../resources +PIC1 = $(INTERMDIR)/pic1.bin +PIC2 = $(INTERMDIR)/pic2.bin + +ifeq ($(_PLATFORM_),c16) +STARTTRACK = 25 +STARTSECTOR = 13 +INTERLEAVE = 3 +SKEW = 13 +DEMO = $(RESOURCESDIR)/threeve.prg +DEMONAME = "threeve" +else ifeq ($(_PLATFORM_),c128) +STARTTRACK = 19 +STARTSECTOR = 0 +INTERLEAVE = 3 +SKEW = 13 +DEMO = $(RESOURCESDIR)/doom_c128.prg +DEMONAME = "doom c128" +else +STARTTRACK = 18 +STARTSECTOR = 16 +INTERLEAVE = 3 +SKEW = 1 +DEMO = $(RESOURCESDIR)/oxyron_oneder.prg +DEMONAME = "oneder /oxy" +endif + +NAME = standalone + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-$(_PLATFORM_).o +LINK = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +COMPRESS = $(INTERMDIR)/$(NAME)-$(_PLATFORM_).prg +DISKIMAGE = $(BUILDDIR)/$(NAME)-$(_PLATFORM_).d64 + +AS_FLAGS = -I ../../../shared -I ../../include -I $(LOADER) -D EXTCONFIGPATH + +PU_FLAGS = -d -l 0x1c01 -x 0x1c01 -i 1 + + +default: diskimage + + +tellarch: + @$(ECHO) $(ARCH) + + +loader: $(LOADER) + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) lib + + +assemble: $(ASSEMBLE) + +$(ASSEMBLE): $(SOURCE) $(LOADERCFG) + $(MKDIR) $(BUILDDIR) + $(MKDIR) $(INTERMDIR) +ifeq ($(_PLATFORM_),c64) + $(AS) $(AS_FLAGS) -t c64 -Wa -D PLATFORM=64 -o $@ $< +else ifeq ($(_PLATFORM_),c128) + $(AS) $(AS_FLAGS) -t c128 -Wa -DPLATFORM=128 -o $@ $< +else + $(AS) $(AS_FLAGS) -t c16 -Wa -D PLATFORM=16 -o $@ $< +endif + + +link: $(LINK) + +$(LINK): Linkfile-$(_PLATFORM_) $(ASSEMBLE) $(LOADER) + $(LD) -o $@ -C $^ + + +compress: $(COMPRESS) + +$(COMPRESS): $(LINK) $(PU) +ifeq ($(_PLATFORM_),c64) + $(PU) $(PU_FLAGS) -c64 -g 0x37 $< $@ +else ifeq ($(_PLATFORM_),c128) + $(PU) $(PU_FLAGS) -c128 $< $@ +else + $(PU) $(PU_FLAGS) -c16 $< $@ +endif + + +diskimage: $(DISKIMAGE) + +$(DISKIMAGE): $(COMPRESS) $(DEMO) $(PIC1) $(PIC2) $(CC1541)/cc1541 + $(CC1541)/cc1541 -v -n "normal is boring" -i plush -S $(INTERLEAVE) -t \ + \ + -f "standalone" -s 8 -w $(COMPRESS) \ + \ + -f $(DEMONAME) -r $(STARTTRACK) -b $(STARTSECTOR) -F -$(SKEW) -w $(DEMO) \ + -f pic1 -r 18 -E -w $(PIC1) \ + -f pic2 -r 18 -E -w $(PIC2) \ + \ + $@ + +ifneq ($(USE_YAPE),0) +run: $(DISKIMAGE) + $(YAPE) "..\..\build\$(NAME)-$(_PLATFORM_).d64" +else +run: $(DISKIMAGE) +ifneq ($(USE_PLUS4EMU),0) + $(EMU) $(realpath $^) +else + $(EMU) $(realpath $^):standalone +endif +run70: $(DISKIMAGE) + $(EMU70) $(realpath $^):standalone +endif + + +$(INTERMDIR)/%.bin: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + +$(CC1541)/cc1541: $(CC1541)/cc1541.c + $(MAKE) -C $(CC1541) cc1541 + +clean: + -$(RM) $(ASSEMBLE) $(LINK) $(COMPRESS) $(DISKIMAGE) + -$(RM) -rf $(INTERMDIR) + -$(RM) $(BUILDDIR)/loader-c64.lib + -$(RM) $(BUILDDIR)/loader-c128.lib + -$(RM) $(BUILDDIR)/loader-c16.lib + -$(RMDIR) $(BUILDDIR) + +distclean: + -$(MAKE) -C $(LOADER_SRC) clean + $(MAKE) -C $(CC1541) clean + +wipe: distclean clean + +$(PU): + $(MAKE) -C $(PU_SOURCE) diff --git a/loader/samples/standalone/loaderconfig.inc b/loader/samples/standalone/loaderconfig.inc new file mode 100755 index 0000000..94bfdbb --- /dev/null +++ b/loader/samples/standalone/loaderconfig.inc @@ -0,0 +1,134 @@ + +OPENFILE_POLLBLOCK_API = 1 +FORCE_ASYNCHRONOUS_BURST_HANDSHAKE = 1 + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 1 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 0 ; include the fileexists call for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 1 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 1 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 1 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/standalone/standalone.s b/loader/samples/standalone/standalone.s new file mode 100644 index 0000000..36f3eaf --- /dev/null +++ b/loader/samples/standalone/standalone.s @@ -0,0 +1,1243 @@ + +; C-64: not compatible with Final Replay, as it resets the load vector after run + +LOAD_BY_KERNAL = 0; to determine KERNAL throughput + +.include "cia.inc" + +.include "basic.inc" +.include "float.inc" +.include "kernal.inc" + +.include "standard.inc" + +.include "loader.inc" + + +.import openfile +.import pollblock + +.importzp loader_zp_first +.importzp loader_zp_last + +.import __DISKIO_LOAD__ +.import __DISKIO_RUN__ +.import __DISKIO_SIZE__ + +.import __ROM_THUNKS_LOAD__ +.import __ROM_THUNKS_RUN__ +.import __ROM_THUNKS_SIZE__ + +.import __RESIDENT_CODE_LOAD__ +.import __RESIDENT_CODE_RUN__ +.import __RESIDENT_CODE_SIZE__ + +DISKIO_SIZE_ALIGNED = ALIGN __DISKIO_SIZE__ + __RESIDENT_CODE_SIZE__, $0100 +RESIDENT_OFFSET = __RESIDENT_CODE_LOAD__ - __RESIDENT_CODE_RUN__ + +POINTERS = $fe + +FILE_NOT_FOUND = $04 +DEVICE_NOT_PRESENT = $05 +LOAD_ERROR = $1d +BREAK_ERROR = $1e + +.if PLATFORM = diskio::platform::COMMODORE_16 + .include "pio.inc" + .include "ted.inc" +.else + .if PLATFORM = diskio::platform::COMMODORE_128 + .include "mmu.inc" + .endif + .include "vic.inc" +.endif + + ldx #loader_zp_last - loader_zp_first + 2; add space for relocateable restorezp return address +: lda loader_zp_first,x + pha + dex + bpl :- + +.if LOAD_BY_KERNAL = 0 + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE + sta MMU_CR + .endif + LOADER_INSTALL + + ; out: a - status + ; x - drive type + ; y - zeropage address containing a pointer to a zero-terminated version string + ; c - error + bcc installed + + ; install error + eor #$ff + adc #$00 + sta insterror + 1 + + jsr restorezp + RESIDENT_OFFSET + jsr new + + ldx #LOAD_ERROR + lda #$40 + ora MSGFLG + sta MSGFLG; enable print I/O error number +insterror: lda #0 + cmp #.lobyte($0100 - diskio::status::DEVICE_INCOMPATIBLE) + bne :+ + ldx #devincomp - strings + jmp printstr +: jmp ERROR9 + 2; print I/O error number and return to basic + +installed: txa + pha + lda #PETSCII_RETURN + jsr BSOUT + + lda COLOR + pha + .if PLATFORM = diskio::platform::COMMODORE_16 + lda #COLOUR_BLUE | INTENSITY_4 + sta COLOR + .else + lda #PETSCII_WHITE + jsr BSOUT + .endif + ldx $00,y + stx POINTERS + 0 + ldx $01,y + stx POINTERS + 1 + ldy #$00 + beq :+ +printinfo: and #$7f; lower case + jsr BSOUT + iny +: lda (POINTERS),y + bne printinfo + pla + sta COLOR +.endif; LOAD_BY_KERNAL = 0 + +.if PLATFORM = diskio::platform::COMMODORE_64 + lda PNTR + beq nonewline + cmp #SCREEN_COLUMNS + beq nonewline +.endif + lda #PETSCII_RETURN + jsr BSOUT + +nonewline: ldx #ntsc - strings +.if PLATFORM = diskio::platform::COMMODORE_16 + lda #PAL_NTSC_MASK + and TED_CTRL2 + bne :+ +.else + lda PALNTS + beq :+ +.endif + ldx #pal - strings +: jsr printstr + + lda #',' + jsr BSOUT + lda #' ' + jsr BSOUT + lda #'#' + jsr BSOUT +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | BASIC_LO | IO_SPACE + sta MMU_CR +.endif + lda #0 + ldx FA + jsr LINPRT +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR +.endif + lda #':' + jsr BSOUT + lda #' ' + jsr BSOUT + pla + and #%00001111 + tay + ldx drivemsgs,y + jsr printstr + lda #PETSCII_RETURN + jsr BSOUT + + ldx #.lobyte(__ROM_THUNKS_SIZE__) +: lda __ROM_THUNKS_LOAD__ - 1,x + sta __ROM_THUNKS_RUN__ - 1,x + dex + bne :- + + lda #.hibyte(__DISKIO_LOAD__) + sta copyresilp + 2 + lda #.hibyte(__DISKIO_RUN__) + sta copyresilp + 5 + ldx #.hibyte(DISKIO_SIZE_ALIGNED) + ldy #$00 +copyresilp: lda a:.lobyte(__DISKIO_LOAD__),y + sta a:.lobyte(__DISKIO_RUN__),y + iny + bne copyresilp + inc copyresilp + 2 + inc copyresilp + 5 + dex + bne copyresilp + +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | BASIC_LO | IO_SPACE + sta MMU_CR +.endif + +.if PLATFORM = diskio::platform::COMMODORE_16 + lda #PAL_NTSC_MASK + and TED_CTRL2 + bne :+ +.else + lda PALNTS + beq :+ +.endif + INT32TOFAC numccpal + jmp :++ +: INT32TOFAC numccntsc +: ldx #.lobyte(cycspersec) + ldy #.hibyte(cycspersec) + jsr MOV2M + + lda FA + sta driveno + 1 + + lda ILOAD + 0 + sta prvload + 1 + lda ILOAD + 1 + sta prvload + 2 + lda #.lobyte(load) + sta ILOAD + 0 + lda #.hibyte(load) + sta ILOAD + 1 + + lda #.lobyte(ramirq) + sta IRQ_VECTOR + 0 + lda #.hibyte(ramirq) + sta IRQ_VECTOR + 1 + + jsr restorezp + RESIDENT_OFFSET + jsr new + +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda #.lobyte(~CIA_VIC2_BANK_OUTPUT) + ora CIA2_PRA + sta CIA2_PRA + lda #.lobyte(~CIA_SERIAL_DATA_IN_OUTPUT | CIA_SERIAL_CLK_IN_OUTPUT) + sta CIA2_DDRA +.endif + clc + rts; return to BASIC interpreter + +new: tsx + stx sp + 1 + lda STACK + $f9 + sta st0 + 1 + lda STACK + $fa + sta st1 + 1 + jsr NEW +st0: lda #0 + sta STACK + $f9 +st1: lda #0 + sta STACK + $fa +sp: ldx #0 + txs + rts + +printstr: lda strings,x + bne :+ + rts +: jsr BSOUT + inx + bne printstr + rts + +numccntsc: CYCLES_PER_SECOND_NTSC +numccpal: CYCLES_PER_SECOND_PAL + +drivemsgs: .byte msg1541 - strings - 1 + .byte msg1541 - strings, msg1541c - strings, msg1541ii - strings, msg1541u - strings + .byte msg1570 - strings, msg1571 - strings, msg1571cr - strings, msg1581 - strings, msgfd2000 - strings, msgfd4000 - strings + +strings: +.if PLATFORM = diskio::platform::COMMODORE_16 +pal: .byte "pal", 0 +ntsc: .byte "ntsc", 0 +.else +.if NTSC_COMPATIBILITY +pal: .byte "pal - warning: ntsc_compatibility slows " + .byte "down pal speed", 0 +ntsc: .byte "ntsc", 0 +.else +pal: .byte "pal", 0 +ntsc: .byte "ntsc - warning: ntsc_compatibility is " + .byte "not enabled in loader config", 0 +.endif +.endif + +msg1541: .byte "cbm 1541", 0 +msg1541c: .byte "cbm 1541-c", 0 +msg1541ii: .byte "cbm 1541-ii", 0 +msg1541u: .byte "1541u", 0 +msg1570: .byte "cbm 1570", 0 +msg1571: .byte "cbm 1571", 0 +msg1571cr: .byte "cbm 1571cr", 0 +msg1581: .byte "cbm 1581", 0 +msgfd2000: .byte "cbm fd 2000", 0 +msgfd4000: .byte "cbm fd 4000", 0 + +devincomp: .byte "device incompatible", 0 +uninstlmsg: .byte "uninstalling loader", 0 + +.segment "ROM_THUNKS" + +load: jsr enableram + jmp doload + +prevload: jsr enablerom +prvload: jmp $0000 + +irq: jsr enableram + jsr irqsub + jsr enablerom +.if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_IRR + sta TED_IRR +.elseif PLATFORM = diskio::platform::COMMODORE_128 + bit CIA1_ICR +.endif + jmp KPREND + +romjsr: stx :+ + 1 + sty :+ + 2 + tax + jsr enablerom +: jsr $0000 + jmp enableram + +clr: jsr enablerom + jsr CLR + jsr enableram + jmp clrst0 + +fromint32: jsr enablerom + + jsr INT24TOMANTISSA +cyclesmsb: lda #0 + sta FACHO + jsr NORMALIZE + jmp MOVAF; fac#2 = fac#1 + +time: jsr fromint32; fac#2 = numcycles + + lda #.lobyte(cycspersec) + ldy #.hibyte(cycspersec) + jsr FDIVM; fac#1 = fac#2 / cycles per second + jsr MUL10; fac#1 *= 10 + + ; seconds = numcycles / cycles per second + jmp print1dec + +throughput: jsr fromint32; fac#2 = numbytes + + lda #.lobyte(floatbuff) + jsr divide; KB/s = numbytes / seconds + +print1dec: ldx #.lobyte(floatbuff) + ldy #.hibyte(floatbuff) + jsr MOV2M + + jsr FLPINT; fac#1 to int + jsr GIVAYF; int to fac#1 + jsr DIV10; fac#1 /= 10 + + jsr FOUT + jsr STROUT + jmp enableram + +divide: ldy #.hibyte(floatbuff) + jsr FDIVM; fac#1 = fac#2 / arg + jsr DIV10; fac#1 /= 10 +.if LOAD_BY_KERNAL = 0 + jmp FADDH; fac#1 += 0.5 +.else + rts +.endif + +speedup: jsr enablerom + jsr CONUPK; fac#2 = throughput + + lda #.lobyte(kernalthrp) + jsr divide; fac#1 = throughput / kernal_throughput + + jsr FLPINT; fac#1 -> int (a/y) + + ; fall through +.if PLATFORM = diskio::platform::COMMODORE_16 +enableram: sta TED_RAM_ENABLE + rts +enablerom: sta TED_ROM_ENABLE + rts +.elseif PLATFORM = diskio::platform::COMMODORE_128 +enableram: ldx #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE + stx MMU_CR + rts +enablerom: pha + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | BASIC_LO | IO_SPACE + sta MMU_CR + pla + rts + +dspp: jsr enablerom + jsr DSPP; put a char on the screen + jmp enableram + +print: jsr enablerom + jsr PRINT + jmp enableram + +fetch: jsr enablerom + jsr FETCH + jmp enableram +.else +enableram: ldx #MEMCONFIG_IO + stx IO_PORT + rts +enablerom: pha + lda #MEMCONFIG_IO_KERNAL_BASIC + sta IO_PORT + pla + rts + +dspp: jsr enablerom + jsr DSPP; put a char on the screen + jmp enableram +.endif + +enableromc: jsr enablerom + cli + rts + +cycspersec: .res 5 +floatbuff: .res 5 + +kernalthrp: .byte $7f, $47, $de, $23, $10; .39 kB/s + +.segment "RESIDENT_CODE" + +doload: sta VERCK; load/verify + +.if LOAD_BY_KERNAL + +.if PLATFORM = diskio::platform::COMMODORE_16 +; note: time measurement does not work without coupled timers due to no interrupts, and timers are horribly broken in VICE +screen = $0800 +numbytes = $b45a; threeve +.else +screen = $0400 +numbytes = $c7ff; oneder +.endif + jsr initstat + lda VERCK + + ldx #.lobyte(NLOAD) + ldy #.hibyte(NLOAD) + jsr romjsr + + php + pha + txa + pha + tya + pha + + lda #.lobyte(numbytes) + sta numbyteslo + lda #.hibyte(numbytes) + sta numbyteshi + jsr getstat + + lda #PETSCII_RETURN + jsr chrout + + lda numcycles + 0 + ora numcycles + 1 + ora numcycles + 2 + ora cyclesmsb + 1 + bne :+ + inc numcycles + 0; avoid division by 0 + +: lda numcycles + 0 + ldx numcycles + 1 + ldy numcycles + 2 + sec + jsr time + lda #' ' + jsr chrout + lda #'s' + jsr chrout + lda #PETSCII_RETURN + jsr chrout + + clc + lda #2; include load address + adc numbyteslo + ldx numbyteshi + bcc :+ + inx +: ldy #0 + sty cyclesmsb + 1 + sec + jsr throughput + lda #' ' + jsr chrout + lda #'k' + jsr chrout + lda #'b' + jsr chrout + lda #'/' + jsr chrout + lda #'s' + jsr chrout + + lda #PETSCII_RETURN + jsr chrout + + lda floatbuff + 0 + sta screen + 0 + lda floatbuff + 1 + sta screen + 1 + lda floatbuff + 2 + sta screen + 2 + lda floatbuff + 3 + sta screen + 3 + lda floatbuff + 4 + sta screen + 4 + + pla + tay + pla + tax + pla + plp + jmp enablerom; return to BASIC interpreter +.endif; LOAD_BY_KERNAL + +.if 0 + lda #$7f + and MSGFLG + sta MSGFLG; no messages (non-direct mode) +.endif + lda FA + cmp #8 + bcc jmprevload + cmp #31 + bcs jmprevload + + ldy #0 + lda (FNADR),y + cmp #'$' + bne :++ + + lda #uninstlmsg - strings + ldx #.lobyte(printstr) + ldy #.hibyte(printstr) + jsr romjsr + +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda #CIA_VIC2_BANK_OUTPUT + sta CIA2_DDRA + and CIA2_PRA + sta CIA2_PRA +.endif + LOADER_UNINSTALL + + lda #4; need some delay before + ldx #0; the drive has completed + ldy #0; its startup check +: dex + bne :- + dey + bne :- + sec + sbc #1 + bne :- + +jmprevload: lda VERCK; load/verify + jmp prevload + +: ldx #loader_zp_last - loader_zp_first + 2; add space for relocateable restorezp return address +: lda $00,x + pha + dex + bpl :- + + lda CINV + 0 + sta previrqlo + 1 + lda CINV + 1 + sta previrqhi + 1 + + lda FA +driveno: cmp #0 + bne :+; only one drive can load + lda VERCK + beq :++; verify is not implemented +: sec + lda #diskio::status::GENERIC_KERNAL_ERROR + jmp error +: + ; convert file name + ldy #0 +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #FNADR + ldx FNBANK + jsr fetch +.else + lda (FNADR),y +.endif + pha + iny +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #FNADR + ldx FNBANK + jsr fetch +.else + lda (FNADR),y +.endif + tax + pla + cmp #'0' + bne :++ + cpx #':' + bne :++ + clc + lda #2 + adc FNADR + sta FNADR + bcc :+ + inc FNADR + 1 +: dec FNLEN + dec FNLEN + lda #0 +: php + + ldy FNLEN + cpy #FILENAME_MAXLENGTH + bcs :+ + lda #0 + sta filename,y + SKIPWORD +: ldy #FILENAME_MAXLENGTH + dey +: +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #FNADR + ldx FNBANK + jsr fetch +.else + lda (FNADR),y +.endif + sta filename,y + dey + bpl :- + + plp + bne :++ + sec + lda FNADR + sbc #2 + sta FNADR + bcs :+ + dec FNADR + 1 +: inc FNLEN + inc FNLEN +: + bit MSGFLG + bpl :+ + + ldx #.lobyte(LUKING); searching for + ldy #.hibyte(LUKING) + jsr romjsr + jsr cursoron + +.if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_IMR + pha +.endif + lda CINV + 1 + cmp #.hibyte(KERNAL_ROM) + bcc :+ + sei +.if PLATFORM = diskio::platform::COMMODORE_16 + lda #COUNTER_3_IRQ + sta TED_IMR +.endif + lda #.lobyte(irq) + sta CINV + lda #.hibyte(irq) + sta CINV + 1 + cli +: +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda #CIA_VIC2_BANK_OUTPUT + sta CIA2_DDRA + and CIA2_PRA + sta CIA2_PRA +.endif + ldx #.lobyte(filename) + ldy #.hibyte(filename) + lda #0 + sta STATUS + cmp SA; secondary address + bcc :+ + lda MEMUSS + sta loadaddrlo + lda MEMUSS + 1 + sta loadaddrhi +: jsr openfile; this returns immediately + +waitopen: jsr pollblock + bcc :+ + jmp error +: tax + bne waitopen + + lda #OPC_BIT_ABS + bit MSGFLG + bpl :+ + + jsr cursoroff + + ldx #.lobyte(LODING) + ldy #.hibyte(LODING) + jsr romjsr + lda #' ' + jsr chrout + lda loadaddrlo + ldx loadaddrhi + jsr printhex + lda #'-' + jsr chrout + lda endaddrlo + ldx endaddrhi + jsr printhex + + jsr cursoron + + lda #OPC_JSR_ABS; opt +: sta loadloop + + lda #STOP_COLUMN +.if PLATFORM = diskio::platform::COMMODORE_16 + sta PIO_KEYBOARD_COLUMN +.else + sta CIA1_PRA +.endif + ; main loading loop +loadloop: jsr initstat + +.if PLATFORM = diskio::platform::COMMODORE_128 + sei +.endif + jsr pollblock +.if PLATFORM = diskio::platform::COMMODORE_128 + cli +.endif + bcs :+ + tax + bne loadloop + beq :++; jmp + +: pha + jsr getstat + pla + sec +: php + pha + clc + lda #256 - 4 + sei + adc PNTR + sta PNTR + tay + lda endaddrhi + jsr hexnib2scr + lda endaddrlo + jsr hexnib2scr + sty PNTR + lda #OPC_BIT_ABS + sta loadloop + pla + plp + +.if PLATFORM = diskio::platform::COMMODORE_16 + sta TED_KEYBOARD_LATCH + bit TED_KEYBOARD_LATCH +.else + bit CIA1_PRB +.endif + bpl break + + bcc loadloop + + cmp #diskio::status::OK + beq :+ + jmp error + +break: jsr closefile + + lda #BREAK_ERROR + jmp error + +: bit MSGFLG + bmi :+ + + clc + jmp noerror + +: sec + lda endaddrlo + sta VARTAB + sta EAL + sbc loadaddrlo + sta numbyteslo + lda endaddrhi + sta VARTAB + 1 + sta EAH + sbc loadaddrhi + sta numbyteshi + + clc + lda #2; load address + adc numbyteslo + sta numbyteslo + bcc :+ + inc numbyteshi +: + tsx + stx clrsp + 1 + lda STACK + $f9 + sta clrst0 + 1 + lda STACK + $fa + sta clrst1 + 1 + jmp clr +clrst0: lda #0 + sta STACK + $f9 +clrst1: lda #0 + sta STACK + $fa +clrsp: ldx #0 + txs + + jsr cursoroff + + lda #':' + jsr chrout + + lda numcycles + 0 + ora numcycles + 1 + ora numcycles + 2 + ora cyclesmsb + 1 + bne :+ + inc numcycles + 0; avoid division by 0 + +: lda numcycles + 0 + ldx numcycles + 1 + ldy numcycles + 2 + sec + jsr time + lda #'s' + jsr chrout + lda #',' + jsr chrout + + lda numbyteslo + ldx numbyteshi + ldy #0 + sec + jsr throughput + + lda #'k' + jsr chrout + lda #'b' + jsr chrout + lda #'/' + jsr chrout + lda #'s' + jsr chrout + lda #',' + jsr chrout + lda #' ' + jsr chrout + + lda #.lobyte(floatbuff) + ldy #.hibyte(floatbuff) + jsr speedup + tya + ldx #$ff +: inx + sec + tay + sbc #10 + bcs :- + tya + pha + txa + beq :+ + adc #'0' + jsr chrout +: pla + clc + adc #'0' + jsr chrout + lda #'x' + jsr chrout + + clc +bccnoerror: bcc noerror + +error: pha + jsr cursoroff + pla + sec + ; fall through + +noerror: ; carry is clear when branching here +.if PLATFORM <> diskio::platform::COMMODORE_16 + pha + lda #.lobyte(~CIA_VIC2_BANK_OUTPUT) + ora CIA2_PRA + sta CIA2_PRA + lda #.lobyte(~CIA_SERIAL_DATA_IN_OUTPUT | CIA_SERIAL_CLK_IN_OUTPUT) + sta CIA2_DDRA + pla +.endif + sei + bit MSGFLG + bpl :+ + + tax +previrqlo: lda #0 + sta CINV + 0 +previrqhi: lda #0 + sta CINV + 1 +.if PLATFORM = diskio::platform::COMMODORE_16 + pla + and #.lobyte(~IRQ_RASTERLINE_MSB) + sta TED_IMR +.endif + txa + +: jsr restorezp + + bcc loadsucces + tax + cpx #BREAK_ERROR + beq :+ + lda #LOAD_ERROR +: cpx #diskio::status::FILE_NOT_FOUND + bne :+ + lda #FILE_NOT_FOUND + +: cpx #diskio::status::DEVICE_NOT_PRESENT + bne :+ + + ; switch back to KERNAL loading +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda #CIA_VIC2_BANK_OUTPUT + and CIA2_PRA + sta CIA2_PRA +.endif + lda prvload + 1 + sta ILOAD + 0 + lda prvload + 2 + sta ILOAD + 1 + jmp jmprevload; note: searching for is printed again + +: sec + +loadsucces: ldx EAL + ldy EAH + jmp enableromc; return to BASIC interpreter + +chrout: ldx #.lobyte(BSOUT) + ldy #.hibyte(BSOUT) + jmp romjsr + +printhex: pha + txa + pha + lda #'$' + jsr chrout + pla + jsr printnum + pla +printnum: pha + lsr + lsr + lsr + lsr + jsr printdigit + pla + and #%00001111 +printdigit: cmp #10 + bcs :+ + adc #'0' + SKIPWORD +: adc #'a' - 10 - $01 + jmp chrout + +hexnib2scr: tax + lsr + lsr + lsr + lsr + jsr :+ + txa + and #%00001111 + +: cmp #10 + bcs :+ + adc #'0' + SKIPWORD +: sbc #9 +.if PLATFORM = diskio::platform::COMMODORE_128 + bit MODE + bpl :++ + cmp #'0' + bcs :+ + adc #'a' - 1 +: jsr print + SKIPWORD +: +.endif + sta (PNT),y + iny + rts + +initstat: +.if PLATFORM = diskio::platform::COMMODORE_16 + sei + lda #0 + sta cycles_hi + sta TED_COUNTER3_LO + sta TED_COUNTER3_HI + cli +.else + lda #$ff + sta CIA2_TA_LO + sta CIA2_TA_HI + sta CIA2_TB_LO + sta CIA2_TB_HI + lda #FORCE_LOAD | CONTINUOUS | COUNT_TA_UNDF | TIMER_START + sta CIA2_CRB + lda CIA2_CRA + and #.lobyte(~(COUNT_CNT | ONE_SHOT)) + ora #FORCE_LOAD | CONTINUOUS | TIMER_START + sta CIA2_CRA +.endif + rts + +getstat: +.if PLATFORM = diskio::platform::COMMODORE_16 + sei +: ldx TED_COUNTER3_HI + lda TED_COUNTER3_LO + cpx TED_COUNTER3_HI + bne :- + clc + eor #$ff + adc #1 + sta numcycles + 0 + txa + eor #$ff + adc #0 + sta numcycles + 1 + lda cycles_hi + sta numcycles + 2 + cli +.else + lda #.lobyte(~TIMER_START) + and CIA2_CRA + sta CIA2_CRA + lda #TIMER_STOP + sta CIA2_CRB + + sec + lda #$ff - 31; subtract overhead + sbc CIA2_TA_LO + sta numcycles + 0 + lda #$ff + sbc CIA2_TA_HI + sta numcycles + 1 + lda #$ff + sbc CIA2_TB_LO + sta numcycles + 2 + lda #$ff + sbc CIA2_TB_HI + sta cyclesmsb + 1 +.endif + rts + +cursoron: +.if PLATFORM = diskio::platform::COMMODORE_16 + clc + lda PNT + 0 + adc PNTR + sta TED_CURSOR_LO + lda PNT + 1 + and #$03 + adc #$00 + sta TED_CURSOR_HI + rts + +cursoroff: lda #$ff + sta TED_CURSOR_LO + sta TED_CURSOR_HI + rts +.else + lda #0 + sta BLNSW +.if PLATFORM = diskio::platform::COMMODORE_128 + ldx #.lobyte(CURSORON) + ldy #.hibyte(CURSORON) + jsr romjsr +.endif +: rts + +cursoroff: inc BLNSW + lda BLNON ; Flag: Cursor Status + beq :- + lda GDBLN ; Character under Cursor while Cursor Inverted + ldx GDCOL ; Background Color under Cursor + ldy #$00 + sty BLNON ; Flag: Cursor Status + jmp dspp +.endif + +restorezp: sta abuf + 1 + php + pla + tay + + tsx + txa + clc + adc #loader_zp_last - loader_zp_first + 1 + tax + pla + sta a:STACK + 3,x + pla + sta a:STACK + 4,x + ldx #loader_zp_first +: pla + sta $00,x + inx + cpx #loader_zp_last + 1 + bne :- + + tya + pha + plp +abuf: lda #0 + rts + +irqsub: +.if PLATFORM = diskio::platform::COMMODORE_16 + inc cycles_hi + lda #COUNTER_3_IRQ + sta TED_IRR +.else + lda #OPC_RTS + sta irqsubend + jsr ramirq + lda #OPC_RTI + sta irqsubend +.endif + rts + +ramirq: sta irqabuffer + 1 +.if PLATFORM = diskio::platform::COMMODORE_16 + inc cycles_hi + lda #COUNTER_3_IRQ + sta TED_IRR +.else + .if PLATFORM = diskio::platform::COMMODORE_128 + lda MMU_CR + sta irqmmubuff + 1 + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE + sta MMU_CR + lda #RASTER_IRQ + sta VIC2_IRR + bit MODE + bmi irqabuffer + .endif + ; blink cursor: copied from $ea34 (KEY + 3) and modified + lda BLNSW ; Flag: Cursor blink + bne noblink + dec BLNCT ; Timer: Count down for Cursor blink toggle + bne noblink + + sty irqybuffer + 1 + + lda #20 + sta BLNCT ; Timer: Count down for Cursor blink toggle + ldy PNTR ; Cursor Column on current Line + lda GDCOL ; Background Color under Cursor + pha + lda (PNT),Y ; Pointer: Current Screen Line Address + lsr BLNON ; Flag: Cursor Status + bcs :+ + inc BLNON ; Flag: Cursor Status + sta GDBLN ; Character under Cursor while Cursor Inverted + lda PNT ; Pointer: Current Screen Line Address + sta USER ; Pointer: Current Color RAM Location + lda PNT + 1 ; Pointer: Current Screen Line Address + and #$03 + ora #$d8 + sta USER + 1 ; Pointer: Current Color RAM Location + lda (USER),Y ; Pointer: Current Color RAM Location + sta GDCOL ; Background Color under Cursor + pla + lda COLOR ; Current Character Color code + pha + lda GDBLN ; Character under Cursor while Cursor Inverted +: eor #$80 + sta (PNT),Y ; Pointer: Current Screen Line Address + pla + sta (USER),Y ; Pointer: Current Color RAM Location + +irqybuffer: ldy #0 + +noblink: + .if PLATFORM = diskio::platform::COMMODORE_64 + bit CIA1_ICR + .endif; PLATFORM = diskio::platform::COMMODORE_64 + .if PLATFORM = diskio::platform::COMMODORE_128 +irqmmubuff: lda #0 + sta MMU_CR + .endif +.endif +irqabuffer: lda #0 +irqsubend: rti + +.if PLATFORM = diskio::platform::COMMODORE_16 +cycles_hi: .res 1 +.endif + +numcycles: .res 3 +numbyteslo: .res 1 +numbyteshi: .res 1 + +filename: .res FILENAME_MAXLENGTH + .byte 0 diff --git a/loader/samples/test/Linkfile b/loader/samples/test/Linkfile new file mode 100755 index 0000000..f0ec7c0 --- /dev/null +++ b/loader/samples/test/Linkfile @@ -0,0 +1,35 @@ + +MEMORY +{ + LOADERZP: start = $4c, size = $44, type = rw; # must not overlap STATUS ($90) + ZPRAM: start = $fa, size = $06, type = rw; + RAM: start = $1300, size = $bd00, type = rw; # C-128 has BASIC variables up to $12ff, Plus/4 has screen and colours at $0800-$1000 + RAM2: start = $2000, size = $d000, type = rw; +} + +SEGMENTS +{ + ZEROPAGE: load = ZPRAM, type = zp; + + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro, optional = yes; + DATA: load = RAM, type = rw, optional = yes; + BSS: load = RAM, type = rw, optional = yes; + + COLRAM: load = RAM2, type = bss, start = $5000, optional = yes, define = yes; # $1000 bytes on Plus/4 (SPRITES segment not used) + SPRITES: load = RAM2, type = bss, start = $5c00, optional = yes, define = yes; # no SPRITESHI segment: the sprites are always in this bank + BITMAP: load = RAM2, type = bss, start = $6000, optional = yes, define = yes; # overlay with DISKIO_INSTALL + + SWAPBUFFER: load = RAM2, type = bss, start = $8000, optional = yes, define = yes; + + BITMAPHI: load = RAM2, type = bss, start = $c000, optional = yes, define = yes; # also location of original IEEE-488 interface code + COLRAMHI: load = RAM2, type = bss, start = $f000, optional = yes, define = yes; # not used on Plus/4 + + DISKIO_ZP: load = LOADERZP, type = zp, define = yes; + DISKIO_PLUGIN_ZP: load = LOADERZP, type = zp, define = yes, optional = yes; + DISKIO: load = RAM, start = $3000, define = yes; + DISKIO_PLUGIN: load = RAM, define = yes, optional = yes; + DISKIO_INSTALL: load = RAM, start = $4000, define = yes; # fire and forget + + END: load = RAM, align = $0100; +} diff --git a/loader/samples/test/Makefile b/loader/samples/test/Makefile new file mode 100755 index 0000000..341b0b1 --- /dev/null +++ b/loader/samples/test/Makefile @@ -0,0 +1,612 @@ + +D71 = d71 +#D71 = d64 # use this if ONLY_1541_AND_COMPATIBLE != 0 + +ifeq ($(PLATFORM),) +_PLATFORM_ = c64 +else ifeq ($(PLATFORM),c128) +_PLATFORM_ = c128 +else ifeq ($(PLATFORM),c16) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),c116) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),plus4) +_PLATFORM_ = c16 +else +_PLATFORM_ = $(PLATFORM) +endif + +ifeq ($(USE_VICE),) +USE_VICE = 0 +endif + + +ifneq ($(_PLATFORM_),c64) +ifneq ($(_PLATFORM_),c128) +ifneq ($(_PLATFORM_),c16) +$(error invalid platform $(_PLATFORM_) specified) +endif +endif +endif + + +ARCH = $(shell uname | tr "[a-z]" "[A-Z]" | tr -c -d "[A-Z]") +HOME := $(shell echo ~) + +ifneq ($(findstring CYGWINNT,$(ARCH)),) + ifeq (CYGWINNT,$(ARCH)) +ARCH = WIN32 + else +ARCH = WIN64 + endif +endif +ifneq ($(findstring DARWIN,$(ARCH)),) +ARCH = MACOSX +endif + + +ifeq ($(_PLATFORM_),c16) + ifeq ($(USE_VICE),0) + ifneq ($(findstring WIN,$(ARCH)),) +USE_PLUS4EMU = 0 +USE_YAPE = 1 + else +USE_PLUS4EMU = 1 +USE_YAPE = 0 + endif + else +USE_PLUS4EMU = 0 +USE_YAPE = 0 + endif +else +USE_PLUS4EMU = 0 +USE_YAPE = 0 +endif + + +ifeq ($(_PLATFORM_),c16) + ifeq ($(ARCH),MACOSX) + # MacOSX, these programs must be installed as applications +VICE = xplus4 +VICE128 = $(VICE) +PLUS4EMU = open /Applications/plus4emu.app --args # -cfg "$(HOME)/Library/Application Support/plus4emu/config/P4_64k_NTSC.cfg" + else +VICE = xplus4 +PLUS4EMU = plus4emu + ifeq ($(ARCH),WIN64) +YAPE = YapeWin64 + else +YAPE = Yape + endif + endif +else ifeq ($(_PLATFORM_),c128) +VICE = x128 +VICE128 = x128 +else +VICE = x64sc +VICE128 = x128 +endif + +ifeq ($(_PLATFORM_),c16) +# for testing, VICE only +DRIVE9TYPE = 1551 +else +DRIVE9TYPE = 0 +endif + +ifneq ($(USE_PLUS4EMU),0) +EMU41 = $(PLUS4EMU) floppy.a.driveType=0 floppy.b.driveType=1 -disk +EMU51 = $(PLUS4EMU) floppy.a.driveType=1 floppy.b.driveType=1 -disk # C-16 only +else +EMU41 = $(VICE) -drive8type 1541 -drive9type $(DRIVE9TYPE) -autostart +EMU51 = $(VICE) -drive8type 1551 -drive9type $(DRIVE9TYPE) -autostart # C-16 only +endif +# the following mode is only supported by VICE and YAPE +EMU81 = $(VICE) -drive8type 1581 -drive9type $(DRIVE9TYPE) -autostart +# the following modes are only supported by VICE +EMU42 = $(VICE) -drive8type 1542 -drive9type $(DRIVE9TYPE) -autostart # 1541-II +EMU70 = $(VICE) -drive8type 1570 -drive9type $(DRIVE9TYPE) -autostart +EMU71 = $(VICE) -drive8type 1571 -drive9type $(DRIVE9TYPE) -autostart +EMU7141 = $(VICE) -drive8type 1571 -drive9type $(DRIVE9TYPE) -autostart # 1571 with .d64 +EMU73 = $(VICE128) -drive8type 1573 -drive9type 0 -autostart # 1571CR +EMU7341 = $(VICE128) -drive8type 1573 -drive9type 0 -autostart # 1571CR with .d64 +EMU2000 = $(VICE) -drive8type 2000 -drive9type 0 -autostart +EMU4000 = $(VICE) -drive8type 4000 -drive9type 0 -autostart + +ifneq ($(USE_PLUS4EMU),0) +EMU941 = $(PLUS4EMU) floppy.a.driveType=0 floppy.b.driveType=0 -disk +EMU951 = $(PLUS4EMU) floppy.a.driveType=1 floppy.b.driveType=0 -disk # C-16 only +else +EMU941 = $(VICE) -drive8type 1541 -drive9type 1541 -autostart +EMU94171 = $(VICE) -drive8type 1541 -drive9type 1571 -autostart +EMU94181 = $(VICE) -drive8type 1541 -drive9type 1581 -autostart +EMU951 = $(VICE) -drive8type 1551 -drive9type 1541 -autostart # C-16 only +endif +# the following modes are only supported by VICE +EMU942 = $(VICE) -drive8type 1542 -drive9type 1541 -autostart # 1541-II +EMU970 = $(VICE) -drive8type 1570 -drive9type 1541 -autostart +EMU971 = $(VICE) -drive8type 1571 -drive9type 1571 -autostart +EMU97141 = $(VICE) -drive8type 1571 -drive9type 1541 -autostart # 1571 with .d64 +EMU973 = $(VICE128) -drive8type 1573 -drive9type 1541 -autostart # 1571CR +EMU97341 = $(VICE128) -drive8type 1573 -drive9type 1541 -autostart # 1571CR with .d64 +EMU981 = $(VICE) -drive8type 1581 -drive9type 1541 -drive10type 1541 -drive11type 1571 -autostart +EMU92000 = $(VICE) -drive8type 2000 -drive9type 1541 -autostart +EMU94000 = $(VICE) -drive8type 4000 -drive9type 1541 -autostart + + +ECHO = echo +PRINTF = printf +TAIL = tail + +MAKE = make +CP = cp +MV = mv +RM = rm -rf +MKDIR = mkdir +CAT = cat + +AS = ca65 +LD = ld65 +C1541 = c1541 + +CC1541 = ../../tools/cc1541/cc1541 +#CC1541 = ../../tools/cc1541/repo4/cc1541/cc1541 +CC1541_SOURCE = ../../tools/cc1541 + + +BITNAX = ../../tools/bitnax-07a8c67/lz + +B2 = ../../tools/b2/b2.exe +B2_SOURCE = ../../tools/b2 + +EXO = ../../tools/exomizer-3.1/src/exomizer +EXO_SOURCE = ../../tools/exomizer-3.1/src + +DOYNAX_LZ = ../../tools/doynamite1.1/lz + +LC = ../../tools/wcrush/wcrush/wcrush +LCSPEED = 6 +LC_SOURCE = ../../tools/wcrush + +LZSA2 = ../../tools/lzsa/lzsa +LZSA2_SOURCE = ../../tools/lzsa + +NC = ../../tools/nucrunch-1.0.1/target/release/nucrunch +NC_SOURCE = ../../tools/nucrunch-1.0.1 + +PU = ../../tools/pucrunch/pucrunch +PU_SOURCE = ../../tools/pucrunch + +SUBSIZER = ../../tools/subsizer-0.7pre1/subsizer +SUBSIZER_SRC = ../../tools/subsizer-0.7pre1/src + +TC = python ../../tools/tinycrunch_v1.2/tc_encode.py + +TS = python3 ../../tools/tscrunch/tscrunch.py + +ZX0 = ../../tools/dali/dali +ZX0_SRC = ../../tools/dali + +CONV = ../../tools/compressedfileconverter.pl + +LOADER = ../../src +LDRBIN = ../../build +LDRINC = ../../include + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate + +RESOURCESDIR = ../resources + + +AS_FLAGS = -I $(LDRINC) -I ../../../shared +PU_FLAGS = -d -l 0x1300 -x 0x1300 -i 0 +ifeq ($(_PLATFORM_),c16) +AS_FLAGS += -t c16 -D PLATFORM=16 +PU_FLAGS += -c16 +else ifeq ($(_PLATFORM_),c128) +AS_FLAGS += -t c128 -D PLATFORM=128 +PU_FLAGS += -c128 +else +AS_FLAGS += -t c64 -D PLATFORM=64 +PU_FLAGS += -c64 -g 0x37 +endif + +EXTENSION_ := prg +EXTENSION_BITNAX := bnx +EXTENSION_BYTEBOOZER2 := b2 +EXTENSION_DOYNAX_LZ := dnx +EXTENSION_EXOMIZER := exo +EXTENSION_LEVELCRUSH := lc +EXTENSION_LZSA2 := lzsa2 +EXTENSION_NUCRUNCH := nc +EXTENSION_PUCRUNCH := pu +EXTENSION_SUBSIZER := ssz +EXTENSION_TINYCRUNCH := tc +EXTENSION_TSCRUNCH := ts +EXTENSION_ZX0 := zx0 + +COMPRESSOR := $(shell grep -v 'DECOMPRESSORS::NONE' ../../include/config.inc | grep 'DECOMPRESSORS::' | sed 's/.*DECOMPRESSORS::\([^; ]*\).*/\1/') +COMPEXT := $(EXTENSION_$(COMPRESSOR)) + + +default: all + +tellarch: + @$(ECHO) $(ARCH) + + +$(BUILDDIR): + $(MKDIR) $@ + +$(INTERMDIR): + $(MKDIR) $@ + + +SHAREDDEPS = $(LOADER)/Makefile \ + $(LDRINC)/config.inc + +LOADERDEPS = $(SHAREDDEPS) \ + $(LOADER)/decompress/bitnaxdecomp.s \ + $(LOADER)/decompress/b2decomp.s \ + $(LOADER)/decompress/doynaxdecomp.s \ + $(LOADER)/decompress/exodecomp.s \ + $(LOADER)/decompress/lcdecomp.s \ + $(LOADER)/decompress/lzsa2decomp.s \ + $(LOADER)/decompress/ncdecomp.s \ + $(LOADER)/decompress/pudecomp.s \ + $(LOADER)/decompress/subsizerdecomp.s \ + $(LOADER)/decompress/tcdecomp.s \ + $(LOADER)/decompress/tsdecomp.s \ + $(LOADER)/decompress/zx0decomp.s \ + $(LOADER)/drives/drivecode-common.inc $(LOADER)/drives/drivecode1541.s $(LOADER)/drives/drivecode1571.s $(LOADER)/drives/drivecode1581.s \ + $(LOADER)/hal/hal.inc $(LOADER)/hal/hal-c64-c128.inc $(LOADER)/hal/hal-c16.inc \ + $(LOADER)/install.s \ + $(LOADER)/resident.s \ + $(LOADER)/customdrivecode.s \ + $(LOADER)/save.s \ + $(LDRINC)/loader.inc + +DISKIMAGEDEPS = Makefile $(CC1541) \ + $(INTERMDIR)/test-$(_PLATFORM_).prg \ + $(INTERMDIR)/pic1.prg $(INTERMDIR)/pic2.prg \ + $(INTERMDIR)/pic1hi.prg $(INTERMDIR)/pic2hi.prg \ + $(INTERMDIR)/pic1.$(COMPEXT) $(INTERMDIR)/pic2.$(COMPEXT) \ + $(INTERMDIR)/pic1hi.$(COMPEXT) $(INTERMDIR)/pic2hi.$(COMPEXT) + + +all: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 \ + $(BUILDDIR)/loadertest-$(_PLATFORM_).d71 \ + $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + + +$(LDRBIN)/loader-$(_PLATFORM_).lib: $(LOADERDEPS) | $(BUILDDIR) $(INTERMDIR) + $(MAKE) -C $(LOADER) lib + + +../../version.inc: + $(MAKE) -C ../.. version.inc + +.PHONY: datetime.inc +datetime.inc: + $(ECHO) ".define DATETIME \"`date -R | cut -d ' ' -f 1-5`\"" > $@ + +$(INTERMDIR)/test-$(_PLATFORM_).o: test.s datetime.inc $(LDRBIN)/loader-$(_PLATFORM_).lib ../../version.inc Makefile | $(BUILDDIR) $(INTERMDIR) + $(AS) $(AS_FLAGS) -o $@ $< + +$(INTERMDIR)/test-$(_PLATFORM_)-uncompressed.prg: Linkfile $(INTERMDIR)/test-$(_PLATFORM_).o $(LDRBIN)/loader-$(_PLATFORM_).lib + $(LD) -o $@ $(INTERMDIR)/test-$(_PLATFORM_).o -C Linkfile -vm -m $@.map -Ln $@.lbl $(LDRBIN)/loader-$(_PLATFORM_).lib + +$(INTERMDIR)/test-$(_PLATFORM_).prg: $(INTERMDIR)/test-$(_PLATFORM_)-uncompressed.prg $(PU) + $(PU) $(PU_FLAGS) $< $@ + + +$(INTERMDIR)/pic1hi.prg: $(RESOURCESDIR)/pic1.bin + $(PRINTF) '\000\300' | $(CAT) - $? > $@ # octal 300 = hex c0 + +$(INTERMDIR)/pic2hi.prg: $(RESOURCESDIR)/pic2.bin + $(PRINTF) '\000\300' | $(CAT) - $? > $@ # octal 300 = hex c0 + +$(INTERMDIR)/%.prg: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + +# Rules to compress data files +$(INTERMDIR)/%.b2: $(INTERMDIR)/%.prg $(B2) + $(B2) $< + $(MV) $<.b2 $@ + +$(INTERMDIR)/%.bnx: $(INTERMDIR)/%.prg $(BITNAX) + $(BITNAX) --bitfire -o $@ $< + +$(INTERMDIR)/%.dnx: $(INTERMDIR)/%.prg $(DOYNAX_LZ) + $(DOYNAX_LZ) -o $@ $< + +$(INTERMDIR)/%.exo: $(INTERMDIR)/%.prg $(EXO) + $(EXO) mem -f $< -o $@ + +$(INTERMDIR)/%.lc: $(INTERMDIR)/%.prg $(LC) + $(LC) $(LCSPEED) $< $@ + $(CONV) lc $< $@ $@ + +$(INTERMDIR)/%.lzsa2: $(INTERMDIR)/%.prg $(LZSA2) + $(LZSA2) -f 2 $< $@ + +$(INTERMDIR)/%.nc: $(INTERMDIR)/%.prg $(NC) + $(NC) $< --auto -o $@ + +$(INTERMDIR)/%.pu: $(INTERMDIR)/%.prg $(PU) + $(PU) -c0 -x 0 $< $@ + +$(INTERMDIR)/%.ssz: $(INTERMDIR)/%.prg $(SUBSIZER) + $(SUBSIZER) -m -f -o $@ $< + +$(INTERMDIR)/%.tc: $(INTERMDIR)/%.prg + $(TC) --inPlace $< $@ + +$(INTERMDIR)/%.ts: $(INTERMDIR)/%.prg + $(TS) -i $< $@ + +$(INTERMDIR)/%.zx0: $(INTERMDIR)/%.prg $(ZX0) + $(ZX0) -o $@ $< + + +ifneq ($(_PLATFORM_),c16) +APPLY_EXTRAS = -r 19 -f "skew1" -w $(RESOURCESDIR)/skew1.prg +endif + +ifeq ($(_PLATFORM_),c128) +DISKIMAGEDEPS += $(RESOURCESDIR)/bootblock.bin +APPLY_BOOTBLOCK = $(TAIL) -c +257 $@ | $(CAT) $(RESOURCESDIR)/bootblock.bin - > $@.boot && $(RM) $@ && $(MV) $@.boot $@ +endif + +ifeq ($(_PLATFORM_),c16) +INTERLEAVE = 4 +D64SS1 = 2 +D64SS2 = 12 +D64SS3 = 16 +D64SS4 = 8 +D64FSNT1 = 3 +D64FSNT2 = 13 +else +INTERLEAVE = 4 +D64SS1 = 8 +D64SS2 = 15 +D64SS3 = 14 +D64SS4 = 11 +D64FSNT1 = 1 +D64FSNT2 = 8 +endif + +$(BUILDDIR)/loadertest-$(_PLATFORM_).d64 $(BUILDDIR)/loadertest-$(_PLATFORM_).g64: $(DISKIMAGEDEPS) + $(CC1541) -v -n "normal is boring" -i plush -d 10 -g $(basename $@).g64 \ + \ + -f "loader test" -s 8 -r 19 -w $(INTERMDIR)/test-$(_PLATFORM_).prg \ + $(APPLY_EXTRAS) \ + \ + -r 2 -S $(INTERLEAVE) \ + -f r1-raw -b $(D64SS1) -F $(D64FSNT1) -E -w $(INTERMDIR)/pic1.prg \ + -f r2-raw -b $(D64SS2) -F $(D64FSNT2) -E -w $(INTERMDIR)/pic2.prg \ + -f p1-pak -b $(D64SS3) -E -w $(INTERMDIR)/pic1.$(COMPEXT) \ + -f p2-pak -b $(D64SS4) -E -w $(INTERMDIR)/pic2.$(COMPEXT) \ + -f lf-loopfile -l r1-raw \ + \ + -r 24 -S 4 \ + -f ab-pic1hiram.bin -w $(INTERMDIR)/pic1hi.prg \ + -f bb-pic2hiram.bin -w $(INTERMDIR)/pic2hi.prg \ + -r 30 -f ap-pic1hiram.pak -w $(INTERMDIR)/pic1hi.$(COMPEXT) \ + -r 34 -f bp-pic2hiram.pak -w $(INTERMDIR)/pic2hi.$(COMPEXT) \ + -f lg-loopfile -l ab-pic1hiram.bin \ + \ + $(basename $@).d64 + $(APPLY_BOOTBLOCK) + @echo Using $(COMPRESSOR) $(COMPEXT) + +ifeq ($(_PLATFORM_),c128) +D71RT1 = 14 +D71RT2 = 14 # 16 +D71SS1 = 13 +D71SS2 = 18 +else +D71RT1 = 22 +D71RT2 = 24 +D71SS1 = 5 +D71SS2 = 8 +endif + +$(BUILDDIR)/loadertest-$(_PLATFORM_).d71: $(DISKIMAGEDEPS) + $(CC1541) -v -n "normal is boring" -i plush -d 17 -S $(INTERLEAVE) \ + -f "loader test" -s 8 -r 11 -w $(INTERMDIR)/test-$(_PLATFORM_).prg \ + \ + -f r1-raw -r $(D71RT1) -b $(D71SS1) -F -17 -c -w $(INTERMDIR)/pic1.prg \ + -f r2-raw -r $(D71RT1) -b $(D71SS2) -F -17 -c -w $(INTERMDIR)/pic2.prg \ + -f p1-pak -r $(D71RT2) -b 6 -c -w $(INTERMDIR)/pic1.$(COMPEXT) \ + -f p2-pak -r $(D71RT2) -c -w $(INTERMDIR)/pic2.$(COMPEXT) \ + -f lf-loopfile -l r1-raw \ + \ + -f ab-pic1hiram.bin -r 29 -c -w $(INTERMDIR)/pic1hi.prg \ + -f bb-pic2hiram.bin -r 29 -c -w $(INTERMDIR)/pic2hi.prg \ + -f ap-pic1hiram.pak -r 31 -c -w $(INTERMDIR)/pic1hi.$(COMPEXT) \ + -f bp-pic2hiram.pak -r 31 -c -w $(INTERMDIR)/pic2hi.$(COMPEXT) \ + -f lg-loopfile -l ab-pic1hiram.bin \ + \ + $@ + $(APPLY_BOOTBLOCK) + @echo Using $(COMPRESSOR) $(COMPEXT) + +$(BUILDDIR)/loadertest-$(_PLATFORM_).d81: $(DISKIMAGEDEPS) + $(CC1541) -v -n "normal is boring" -i plush -d 39 \ + -f "loader test" -r 11 -w $(INTERMDIR)/test-$(_PLATFORM_).prg \ + \ + -r 3 \ + -f r1-raw -w $(INTERMDIR)/pic1.prg \ + -f r2-raw -w $(INTERMDIR)/pic2.prg \ + -f p1-pak -w $(INTERMDIR)/pic1.$(COMPEXT) \ + -f p2-pak -w $(INTERMDIR)/pic2.$(COMPEXT) \ + -f lf-loopfile -l r1-raw \ + \ + -r 6 \ + -f ab-pic1hiram.bin -w $(INTERMDIR)/pic1hi.prg \ + -f bb-pic2hiram.bin -w $(INTERMDIR)/pic2hi.prg \ + -f ap-pic1hiram.pak -w $(INTERMDIR)/pic1hi.$(COMPEXT) \ + -f bp-pic2hiram.pak -w $(INTERMDIR)/pic2hi.$(COMPEXT) \ + -f lg-loopfile -l ab-pic1hiram.bin \ + \ + $@ + $(APPLY_BOOTBLOCK) + @echo Using $(COMPRESSOR) $(COMPEXT) + + +.PHONY: run run41 run51 run41ii run42 run70 run71 run7141 run73 run7341 run81 run2000 run4000 +.PHONY: run9 run941 run94171 run94181 run951 run941ii run942 run970 run971 run97141 run973 run97341 run981 run92000 run94000 + +ifneq ($(USE_YAPE),0) +run run41 run51: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(YAPE) "..\..\build\loadertest-$(_PLATFORM_).d64" +else +run run41: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU41) $(realpath $^) + +run51: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU51) $(realpath $^) +endif + +run41ii run42: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU42) $(realpath $^) + +run70: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU70) $(realpath $^) + +run71: $(BUILDDIR)/loadertest-$(_PLATFORM_).$(D71) + $(EMU71) $(realpath $^) + +run7141: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU7141) $(realpath $^) + +run73: $(BUILDDIR)/loadertest-$(_PLATFORM_).$(D71) + $(EMU73) $(realpath $^) + +run7341: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU7341) $(realpath $^) + +ifneq ($(USE_YAPE),0) +run81: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(YAPE) "..\..\build\loadertest-$(_PLATFORM_).d81" +else +run81: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU81) $(realpath $^) +endif + +run2000: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU2000) $(realpath $^) + +run4000: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU4000) $(realpath $^) + +ifneq ($(USE_YAPE),0) +run9 run941 run951: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(YAPE) /DISK9:"..\..\build\loadertest-$(_PLATFORM_).d64" "..\..\build\loadertest-$(_PLATFORM_).d64" +else +run9 run941: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU941) $(realpath $^) + +run94171: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU94171) $(realpath $^) + +run94181: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU94181) $(realpath $^) + +run951: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU951) $(realpath $^) +endif + +run941ii run942: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU942) $(realpath $^) + +run970: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU970) $(realpath $^) + +run971: $(BUILDDIR)/loadertest-$(_PLATFORM_).$(D71) + $(EMU971) $(realpath $^) + +run97141: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU97141) $(realpath $^) + +run973: $(BUILDDIR)/loadertest-$(_PLATFORM_).$(D71) + $(EMU973) $(realpath $^) + +run97341: $(BUILDDIR)/loadertest-$(_PLATFORM_).d64 + $(EMU97341) $(realpath $^) + +ifneq ($(USE_YAPE),0) +run981: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(YAPE) /DISK9:"..\..\build\loadertest-$(_PLATFORM_).d81" "..\..\build\loadertest-$(_PLATFORM_).d81" +else +run981: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU981) "$(realpath $^):loader test" +endif + +run92000: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU92000) "$(realpath $^):loader test" + +run94000: $(BUILDDIR)/loadertest-$(_PLATFORM_).d81 + $(EMU94000) "$(realpath $^):loader test" + + +clean: + -$(RM) $(INTERMDIR) + -$(RM) $(LDRBIN)/loader-c64.lib + -$(RM) $(LDRBIN)/loader-c128.lib + -$(RM) $(LDRBIN)/loader-c16.lib + +distclean: + -$(RM) $(BITNAX) + -$(RM) $(B2) + -$(RM) $(DOYNAX_LZ) + -$(RM) $(NC) + -$(RM) $(PU) + -$(RM) $(SUBSIZER) + $(MAKE) -C $(CC1541_SOURCE) clean + $(MAKE) -C $(EXO_SOURCE) clean + $(MAKE) -C $(LC_SOURCE) clean + $(MAKE) -C $(LZSA2_SOURCE) clean + $(MAKE) -C $(LOADER) clean + $(MAKE) -C $(NC_SOURCE) clean + $(MAKE) -C $(SUBSIZER_SRC) clean + -$(MAKE) -C $(ZX0_SRC) clean + +wipe: distclean clean + + +$(CC1541): $(CC1541_SOURCE)/cc1541.c + $(MAKE) -C $(CC1541_SOURCE) cc1541 + + +$(BITNAX): + $(CC) $@.c -o $@ + +$(B2): + $(MAKE) -C $(B2_SOURCE) + +$(DOYNAX_LZ): + $(CC) $@.c -o $@ + +$(EXO): + $(MAKE) -C $(EXO_SOURCE) + +$(LC): + $(MAKE) -C $(LC_SOURCE) + +$(LZSA2): + $(MAKE) -C $(LZSA2_SOURCE) + +$(NC): + $(MAKE) -C $(NC_SOURCE) + +$(PU): + $(MAKE) -C $(PU_SOURCE) + +$(SUBSIZER): + $(MAKE) -C $(SUBSIZER_SRC) + +$(ZX0): + $(MAKE) -C $(ZX0_SRC) diff --git a/loader/samples/test/test.s b/loader/samples/test/test.s new file mode 100755 index 0000000..16fe727 --- /dev/null +++ b/loader/samples/test/test.s @@ -0,0 +1,4292 @@ + +TEST_SAVE = 0 +TEST_INVALID_PARAMETERS = 0 +TEST_BUS_LOCK = 0 +NO_SPRITES_OR_IRQ = 0 +VERIFY = 0; verify correct loading by loading twice and comparing +PAUSE_BETWEEN_LOADS = 0; delay value +INSTALL_ONLY = 0 +KEY_FOR_WATCHDOG = 0 +LITTLE_CPU_FOR_LOADER = 0 + +COUPLED_TIMERS = NO_SPRITES_OR_IRQ; Plus/4: As TED timer emulation is horribly broken on VICE, only use coupled + ; timers if there is no other way to measure more than 64K cycles +.include "standard.inc" + +.include "../include/loader.inc"; includes config.inc + +.if !((DIRTRACK = 18) | (DIRTRACK = 17)) + .error "The test application only supports DIRTRACK = 18 or DIRTRACK = 17" +.endif +.if !((DIRTRACK81 = 40) | (DIRTRACK81 = 39)) + .error "The test application only supports DIRTRACK81 = 40 or DIRTRACK81 = 39" +.endif + +.include "../version.inc" +.include "datetime.inc" + +.include "cpu.inc" +.if PLATFORM = diskio::platform::COMMODORE_16 + .include "pio.inc" + .include "ted.inc" +.else + .include "cia.inc" + .include "vic.inc" +.endif + +.if PLATFORM = diskio::platform::COMMODORE_128 + .include "mmu.inc" + .include "vdc.inc" +.endif + +.include "float.inc" +.include "basic.inc" +.include "kernal.inc" + +.macpack cbm +.macpack longbranch + +.segment "ZEROPAGE" : zeropage + +.if PLATFORM = diskio::platform::COMMODORE_16 +IRQSLACK = $03; 0: NTSC +.else +IRQSLACK = $a8; RS232 Input Bit count/Tape temporary +.endif + +POINTERS: .res 6 + +IRQ_SLACK = $00 +KERNAL_FALLBACK_IRQ_SLACK_PARALLEL = $00 +KERNAL_FALLBACK_IRQ_SLACK_BURST = $30 +.if PLATFORM = diskio::platform::COMMODORE_128 +KERNAL_FALLBACK_IRQ_SLACK_SERIAL = $37 +.else +KERNAL_FALLBACK_IRQ_SLACK_SERIAL = $1f +.endif + + +LOAD_TO_UPPER_MEM = LOAD_UNDER_D000_DFFF; on C-16/+4, himem is $8000..$fcff, but it can be loaded to regardless of this setting + +.if LOAD_TO_UPPER_MEM + +.segment "BITMAPHI" +.import __BITMAPHI_LOAD__ +BITMAP = __BITMAPHI_LOAD__ + + .if PLATFORM = diskio::platform::COMMODORE_16 +.segment "COLRAM" +.import __COLRAM_LOAD__ +COLRAM = __COLRAM_LOAD__ + .else +.segment "COLRAMHI" +.import __COLRAMHI_LOAD__ +COLRAM = __COLRAMHI_LOAD__ + .endif + +.else; !LOAD_TO_UPPER_MEM + +.segment "BITMAP" +.import __BITMAP_LOAD__ +BITMAP = __BITMAP_LOAD__ + +.segment "COLRAM" +.import __COLRAM_LOAD__ +COLRAM = __COLRAM_LOAD__ + +.endif + +.if PLATFORM <> diskio::platform::COMMODORE_16 +.segment "SPRITES" +.import __SPRITES_LOAD__ +SPRITES = __SPRITES_LOAD__ + + .if LOAD_TO_UPPER_MEM +SPRITESCR = SPRITES + $0400 + .else +SPRITESCR = COLRAM + .endif +.endif + +TESTNAMEPOS = $0b; x coord + +; cannot define string symbols from the command line with ca65 +.if DECOMPRESSOR = DECOMPRESSORS::BITNAX + .define COMPEXT "BNX" + .define SAFETYMARGIN 0 +.elseif DECOMPRESSOR = DECOMPRESSORS::BYTEBOOZER2 + .define COMPEXT "B2" + .define SAFETYMARGIN 3 +.elseif DECOMPRESSOR = DECOMPRESSORS::DOYNAX_LZ + .define COMPEXT "DNX" + .define SAFETYMARGIN 4 +.elseif DECOMPRESSOR = DECOMPRESSORS::EXOMIZER + .define COMPEXT "EXO" + .define SAFETYMARGIN 3 +.elseif DECOMPRESSOR = DECOMPRESSORS::LEVELCRUSH + .define COMPEXT "LC" + .define SAFETYMARGIN 3 +.elseif DECOMPRESSOR = DECOMPRESSORS::LZSA2 + .define COMPEXT "SA2" + .define SAFETYMARGIN 0 +.elseif DECOMPRESSOR = DECOMPRESSORS::NUCRUNCH + .define COMPEXT "NC" + .define SAFETYMARGIN 5 +.elseif DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH + .define COMPEXT "PU" + .define SAFETYMARGIN 36 +.elseif DECOMPRESSOR = DECOMPRESSORS::SUBSIZER + .define COMPEXT "SSZ" + .define SAFETYMARGIN 2 +.elseif DECOMPRESSOR = DECOMPRESSORS::TINYCRUNCH + .define COMPEXT "TC" + .define SAFETYMARGIN 0 +.elseif DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH + .define COMPEXT "TS" + .define SAFETYMARGIN 0 +.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0 + .define COMPEXT "ZX0" + .define SAFETYMARGIN 0 +.else + .define COMPEXT "PRG" + .define SAFETYMARGIN 0 +.endif + + +; testing macros + +.if VERIFY + .macro VERIFY_PROLOGUE + ; copy away + jsr piccopy + .endmacro + + .macro VERIFY_EPILOGUE + .local verifyok + + jsr piccomp + beq verifyok + jsr verifyfail +verifyok: + .endmacro +.endif + + +.macro MEMCONFIG_BUFFER + .if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_CHARGEN_ADDR + .elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + sta MMU_CR + .else + lda IO_PORT_DIRECTION + pha + lda IO_PORT + .endif + pha +.endmacro + +.macro MEMCONFIG_CHECK + pla + .if PLATFORM = diskio::platform::COMMODORE_16 + eor TED_CHARGEN_ADDR + and #ROM_IS_ENABLED | FORCE_SINGLE_CLOCK + beq :+ + lda #MEMCONFCHNG + jsr error + .elseif PLATFORM = diskio::platform::COMMODORE_128 + cmp MMU_CR + beq :+ + tax ; expected + ldy MMU_CR; actual + lda #MEMCONFCHNG + jsr error + .else + tax + ldy IO_PORT + pla + cpx IO_PORT + bne :+ + tax + ldy IO_PORT_DIRECTION + cpx IO_PORT_DIRECTION + beq :++ +: lda #MEMCONFCHNG + jsr error + .endif +: +.endmacro + +.macro ENABLE_MEMCONFIG_CHECK + lda #$ff + sta memcfgchks +.endmacro + +.macro DISABLE_MEMCONFIG_CHECK + lda #0 + sta memcfgchks +.endmacro + +.macro ENABLE_ALL_ROM + .if PLATFORM = diskio::platform::COMMODORE_16 + sta TED_ROM_ENABLE + .elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | BASIC_LO | IO_SPACE + sta MMU_CR + .else + lda #MEMCONFIG_IO_KERNAL_BASIC + sta IO_PORT + .endif +.endmacro + +.macro ENABLE_ALL_RAM + .if PLATFORM = diskio::platform::COMMODORE_16 + sta TED_RAM_ENABLE + .elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM + sta MMU_CR + .else + lda #MEMCONFIG_ALL_RAM + sta IO_PORT + .endif +.endmacro + +.macro PUSH_MEMCONFIG + .if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_CHARGEN_ADDR + lsr + php + .elseif PLATFORM = diskio::platform::COMMODORE_128 + lda MMU_CR + pha + .else + lda IO_PORT + pha + .endif +.endmacro + +.macro PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + PUSH_MEMCONFIG + ENABLE_ALL_ROM +.endmacro + +.macro PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + PUSH_MEMCONFIG + ENABLE_ALL_RAM +.endmacro + +.macro POP_MEMCONFIG + .if PLATFORM = diskio::platform::COMMODORE_16 + .local enable_ram + + plp + ENABLE_ALL_RAM + bcc enable_ram + ENABLE_ALL_ROM +enable_ram: + .elseif PLATFORM = diskio::platform::COMMODORE_128 + pla + sta MMU_CR + .else + pla + sta IO_PORT + .endif +.endmacro + + +.macro TEST testmacro, filename, redzone + .local redzoneok + + .if TEST_BUS_LOCK + ENTER_BUS_LOCK +: inc CIA2_PRA + dex + bne :- + ror CIA2_PRA + dey + bne :- + LEAVE_BUS_LOCK + .endif; TEST_BUS_LOCK + + DISABLE_MEMCONFIG_CHECK + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + lda looprunlo + sta redzone + POP_MEMCONFIG + ENABLE_MEMCONFIG_CHECK + + MEMCONFIG_BUFFER + .if !NO_SPRITES_OR_IRQ + lda #.lobyte(filename) + sta filenamelo + lda #.hibyte(filename) + sta filenamehi + .endif + testmacro filename + MEMCONFIG_CHECK + + DISABLE_MEMCONFIG_CHECK + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + lda looprunlo + cmp redzone + beq redzoneok + lda #ERRSAFETYMARGIN + ldx #.lobyte(redzone) + ldy #.hibyte(redzone) + jmp error +redzoneok: POP_MEMCONFIG + ENABLE_MEMCONFIG_CHECK + + .if VERIFY + VERIFY_PROLOGUE + testmacro filename + VERIFY_EPILOGUE + .endif + jsr checkkeys +.endmacro + +.macro CHECKLOADADDRESSRAW + .local lodaddfail + .local loadaddrok + + .if LOAD_TO_API + lda #.lobyte(BITMAP + $20) + .else + lda #.lobyte(BITMAP) + .endif + cmp loadaddrlo + bne lodaddfail + lda #.hibyte(BITMAP) + cmp loadaddrhi + beq loadaddrok +lodaddfail: lda #ERRLOADADDR + ldx loadaddrlo + ldy loadaddrhi + jmp error +loadaddrok: +.endmacro + +.macro CHECKLOADADDRESSCOMPD + .local loadaddrok + + ; load address must be > BITMAP address + lda #.lobyte(BITMAP) + cmp loadaddrlo + lda #.hibyte(BITMAP) + sbc loadaddrhi + bcc loadaddrok + lda #ERRLOADADDR + ldx loadaddrlo + ldy loadaddrhi + jmp error +loadaddrok: + +.endmacro + +.macro CHECKENDADDRESSRAW + .if END_ADDRESS_API + .local endaddfail + .local endaddrok + + clc + lda #.lobyte(BITMAP_SIZE) + adc loadaddrlo + eor endaddrlo + bne endaddfail + lda #.hibyte(BITMAP_SIZE) + adc loadaddrhi + cmp endaddrhi + beq endaddrok +endaddfail: lda #ERRENDADDR + ldx endaddrlo + ldy endaddrhi + jmp error +endaddrok: + .endif; END_ADDRESS_API +.endmacro + +.macro CHECKENDADDRESSCOMPD dataoffset + .if END_ADDRESS_API + .local endaddfail + .local endaddrok + + lda endaddrlo + cmp #.lobyte(BITMAP + BITMAP_SIZE + dataoffset) + bcc endaddfail + lda endaddrhi + cmp #.hibyte(BITMAP + BITMAP_SIZE + dataoffset) + beq endaddrok +endaddfail: lda #ERRENDADDR + ldx endaddrlo + ldy endaddrhi + jmp error +endaddrok: + .endif; END_ADDRESS_API +.endmacro + +.macro PRINTTESTNAME testname + .local filllength + .local name + .local nameend + + DISABLE_MEMCONFIG_CHECK + + lda #0 + sta DFLTO + + .if !NO_SPRITES_OR_IRQ + lda #.lobyte(name) + sta testnamelo + lda #.hibyte(name) + sta testnamehi + .endif + ldx #TESTNAMEPOS + ldy #$01 + jsr setplotxy + ldx #.lobyte(name) + ldy #.hibyte(name) + jsr plottext + .if PLATFORM = diskio::platform::COMMODORE_16 + sec + lda #.lobyte(COLRAM + $0c38) + sbc POINTERS + 2 + .else + lda #$10 + .endif + sta filllength + sec + lda #.lobyte(emptytexte) +filllength = * + 1 + sbc #$00 + tax + lda #.hibyte(emptytexte) + sbc #$00 + tay + jsr plottext + + ENABLE_MEMCONFIG_CHECK + jmp nameend + +name: .byte testname, 0 +nameend: +.endmacro + +.macro TESTUNCOMPRESSED testmacro, testname, dataoffset + PRINTTESTNAME testname + + TEST testmacro, pic1unc, BITMAP + BITMAP_SIZE + dataoffset + CHECKLOADADDRESSRAW + CHECKENDADDRESSRAW + + TEST testmacro, pic2unc, BITMAP + BITMAP_SIZE + dataoffset + CHECKLOADADDRESSRAW + CHECKENDADDRESSRAW +.endmacro + +.macro TESTCOMPRESSED testmacro, testname, dataoffset + PRINTTESTNAME .concat(testname, ":", COMPEXT) + + TEST testmacro, pic1compd, BITMAP + BITMAP_SIZE + SAFETYMARGIN + dataoffset + CHECKLOADADDRESSCOMPD + CHECKENDADDRESSCOMPD dataoffset + + TEST testmacro, pic2compd, BITMAP + BITMAP_SIZE + SAFETYMARGIN + dataoffset + CHECKLOADADDRESSCOMPD + CHECKENDADDRESSCOMPD dataoffset +.endmacro + +.macro CONSOLE text + lda #.lobyte(text) + ldy #.hibyte(text) + jsr consoleout +.endmacro + +.macro INITSTAT + jsr initstat +.endmacro + +.macro PRINTSTAT numbytes + DISABLE_MEMCONFIG_CHECK + ldx #.hibyte(numbytes) + ldy #.lobyte(numbytes) + jsr printstat + ENABLE_MEMCONFIG_CHECK +.endmacro + + +.macro MEMCLEAR address, size + lda #.hibyte(size) + ldx #.hibyte(address) + jsr memclear +.endmacro + +.macro MEMCOPY source, dest, size + lda #.hibyte(size) + ldx #.hibyte(source) + ldy #.hibyte(dest) + jsr memcopy +.endmacro + +.macro MEMCOMP buffer1, buffer2, size + lda #.lobyte(size) + sta POINTERS + $00 + lda #.hibyte(size) + ldx #.hibyte(buffer1) + ldy #.hibyte(buffer2) + jsr memcomp +.endmacro + + +.segment "CODE" + + sei + ldx #$ff + txs + +.if USE_2_MHZ + lda #0 + sta VIC2_C128_CLOCK +.endif + +.if (PLATFORM = diskio::platform::COMMODORE_64) & LOAD_TO_UPPER_MEM + IEEE_488_CODE_START = $c800 + IEEE_488_CODE_END = $d000 + IEEE_488_CODE_RELOC = $0800 + + lda IOPEN + 1 + cmp #.hibyte(IEEE_488_CODE_START) + bcc noieeerloc + cmp #.hibyte(IEEE_488_CODE_END) + bcs noieeerloc + + lda #0 + sta POINTERS + sta POINTERS + 2 + lda #.hibyte(IEEE_488_CODE_START) + sta POINTERS + 1 + lda #.hibyte(IEEE_488_CODE_RELOC) + sta POINTERS + 3 + sec + sbc POINTERS + 1 + sta POINTERS + 4 + ldx #.hibyte(IEEE_488_CODE_END - IEEE_488_CODE_START) +relocloop: ldy #0 + lda (POINTERS),y + sta (POINTERS + 2),y + cmp #OPC_JSR_ABS + beq :+ + cmp #OPC_JMP_ABS + beq :+ + cmp #OPC_JMP_ABSI + beq :+ + cmp #OPC_LDA_ABSX + bne noreloc +: ldy #2 + lda (POINTERS),y + ldy #0 + cmp #.hibyte(IEEE_488_CODE_START) + bcc noreloc + cmp #.hibyte(IEEE_488_CODE_END) + bcs noreloc + iny + lda (POINTERS),y + sta (POINTERS + 2),y + iny + lda POINTERS + 4 + adc (POINTERS),y + sta (POINTERS + 2),y +noreloc: tya + sec + adc POINTERS + sta POINTERS + sta POINTERS + 2 + bcc relocloop + inc POINTERS + 1 + inc POINTERS + 3 + dex + bne relocloop + ldx #ISAVE - IOPEN +: lda IOPEN + 1,x + cmp #.hibyte(IEEE_488_CODE_START) + bcc :+ + cmp #.hibyte(IEEE_488_CODE_END) + bcs :+ + adc POINTERS + 4 + sta IOPEN + 1,x +: dex + dex + bpl :-- +noieeerloc: +.endif + +.if PLATFORM = diskio::platform::COMMODORE_16 + ; pucrunch turns off the screen during decompression + lda #DISPLAY_DISABLE + sta TED_CTRL1 + ; YAPE doesn't set FA to the current + ; drive when using autostart + lda FA + and #.lobyte(~$01) + cmp #$08 + beq :+ + lda #$08 + sta FA +: +.endif + lda #0; OPC_BRK + ldx #$40; MMU registers are at $ff00..$ff07, TED registers are at $ff00..$ff3f +: sta $ff00,x; guard against calling ROM + inx ; code with RAM enabled + bne :- + + lda #.lobyte(nmihandler) +.if PLATFORM <> diskio::platform::COMMODORE_16 + sta NMINV + $00 +.endif + sta NMI_VECTORLO + lda #.hibyte(nmihandler) +.if PLATFORM <> diskio::platform::COMMODORE_16 + sta NMINV + $01 +.endif + sta NMI_VECTORHI + + lda #.lobyte(ramirq) + sta IRQ_VECTORLO + lda #.hibyte(ramirq) + sta IRQ_VECTORHI + +.if PLATFORM = diskio::platform::COMMODORE_16 + + ldx #$00 + stx tod_hrs + stx tod_mins + stx tod_secs + stx tod_10s + stx tod_frames + + lda #PAL_NTSC_MASK + and TED_CTRL2 + bne :+ + inx; PAL +: stx palntsc + + jsr waitvbl + lda TED_CHARGEN_ADDR + and #.lobyte(~(CHARGEN_ADDR_MASK | FORCE_SINGLE_CLOCK)) + ora #MAKE_CHARGEN_ADDR(CHARSET_ADDR_UPPERLOWER) + sta TED_CHARGEN_ADDR + lda #COLOUR_BLACK + sta TED_BGCOLOUR + sta TED_BORDERCOLOUR + + .if COUPLED_TIMERS = 0 +calibrate: jsr readctr + stx prevctr3lo + sty prevctr3hi + lda #$00 + sta adjustdiff + jsr docalibrte + sta adjustdiff + jsr docalibrte + cmp #$00 + bne calibrate + cpx #$00 + bne calibrate + cpy #$00 + bne calibrate + .endif + + lda #PALETTE_DEFAULT + sta PALETTE + + CONSOLE startupmsg + + jsr waitvbl + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta TED_CTRL1 + + lda TED_RASTERLINE +: cmp TED_RASTERLINE + beq :- +: ldx TED_RASTERLINE + lda TED_RASTERLINE_MSB + cpx TED_RASTERLINE + bne :- + stx TED_IRQ_RASTERLINE + lsr + lda TED_IMR + and #.lobyte(~IRQ_RASTERLINE_MSB) + bcc :+ + ora #IRQ_RASTERLINE_MSB +: sta TED_IMR + + lda #.lobyte(clockirq) + sta CINV + $00 + lda #.hibyte(clockirq) + sta CINV + $01 + lda #RASTER_IRQ + sta TED_IMR + lda TED_IRR + sta TED_IRR + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + + .if PLATFORM = diskio::platform::COMMODORE_128 + ldx #leaveirqe - leaveirq - 1 +: lda leaveirq,x + sta BUF,x + dex + bpl :- + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR + .else; PLATFORM <> diskio::platform::COMMODORE_128 + lda #MEMCONFIG_IO_KERNAL_BASIC + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + ; detect video standard +: bit VIC2_CTRL1 + bmi :- +: bit VIC2_CTRL1 + bpl :- + lda #$37 +: cmp VIC2_RASTERLINE + bne :- + lda VIC2_CTRL1 + asl + lda #$00 + .if PLATFORM = diskio::platform::COMMODORE_128 + bcc :+ + lda #$ff; 0: NTSC, $ff: PAL +: + .else + rol; 0: NTSC, 1: PAL + .endif + sta palntsc + + ; detect mains frequency + lda #TOD_SET_CLOCK | TIMER_STOP + sta CIA1_CRB + ldx #0 + stx CIA1_TOD_10S + ldy #$ff + sty CIA1_TA_LO + sty CIA1_TA_HI + ldy #TOD_FREQ_60HZ | FORCE_LOAD | CONTINUOUS | TIMER_START + sty CIA1_CRA + lda #CIA_CLR_INTF | TIMERA_IRQ + bit CIA1_ICR + sta CIA1_ICR +: cpx CIA1_TOD_10S + bne haveac + bit CIA1_ICR + beq :- +: bit CIA1_ICR + bne noac + cpx CIA1_TOD_10S + beq :- +haveac: sty CIA1_CRA + ldx CIA1_TOD_10S +: cpx CIA1_TOD_10S + beq :- + lda CIA1_TA_HI + cmp #$50 + lda #TOD_FREQ_60HZ + ldx #60 + bcs :+ + lda #TOD_FREQ_50HZ + ldx #50 +: sta CIA1_CRA + stx mainsfreq + 1 +noac: + jsr waitvbl + lda #DISPLAY_DISABLE + sta VIC2_CTRL1 + lda #VIC2_MAKE_ADDR($0400, CHARSET_ADDR_UPPERLOWER) + sta VIC2_ADDR + lda VIC2_ADDR + lda #COLOUR_BLACK + sta VIC2_BGCOLOUR + sta VIC2_BORDERCOLOUR + + CONSOLE startupmsg + + jsr waitvbl + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta VIC2_CTRL1 + + lda #0 + sta CIA1_TOD_HRS + sta CIA1_TOD_MIN + sta CIA1_TOD_SEC + sta CIA1_TOD_10S + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + + cli + + CONSOLE detectedms + lda #.lobyte(paltext) + ldy #.hibyte(paltext) + ldx palntsc + bne :+ + lda #.lobyte(ntsctext) + ldy #.hibyte(ntsctext) +: jsr consoleout + CONSOLE systemmsg + + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + lda palntsc + beq ntsckbs + INT32TOFAC numccpal; fac#1 = cyclespersecond + jmp :+ +ntsckbs: INT32TOFAC numccntsc; fac#1 = cyclespersecond +: jsr DIV10; fac#1 /= 10 + ldx #.lobyte(cyclesperd) + ldy #.hibyte(cyclesperd) + jsr MOV2M; cyclesperd = fac#1 = cycles per 1/10 seconds + + lda #.hibyte(100) + ldy #.lobyte(100) + jsr GIVAYF; fac#1 = 100 + ldx #.lobyte(hundred) + ldy #.hibyte(hundred) + jsr MOV2M; hundred = fac#1 = 100 + + POP_MEMCONFIG + +.if PLATFORM <> diskio::platform::COMMODORE_16 + CONSOLE detectedms + lda #0 +mainsfreq: ldx #0 + jsr printword + CONSOLE acfreqmsg +.endif + lda #.hibyte(__DISKIO_INSTALL_SIZE__) + jsr tohex + stx inssizebyt + sta inssizebyt + 1 + lda #.lobyte(__DISKIO_INSTALL_SIZE__) + jsr tohex + stx inssizebyt + 2 + sta inssizebyt + 3 + CONSOLE inssizemsg + + lda #.hibyte(__DISKIO_SIZE__) + jsr tohex + stx ressizebyt + sta ressizebyt + 1 + lda #.lobyte(__DISKIO_SIZE__) + jsr tohex + stx ressizebyt + 2 + sta ressizebyt + 3 + CONSOLE ressizemsg + +.if TEST_SAVE + .import __DISKIO_PLUGIN_SIZE__ + + lda #.hibyte(__DISKIO_PLUGIN_SIZE__) + jsr tohex + stx trnsizebyt + sta trnsizebyt + 1 + lda #.lobyte(__DISKIO_PLUGIN_SIZE__) + jsr tohex + stx trnsizebyt + 2 + sta trnsizebyt + 3 + CONSOLE trnsizemsg +.endif + + CONSOLE installmsg + lda #0 + ldx FA + jsr printword + CONSOLE installrst + + lda #.lobyte(brkhandler) + sta CBINV + lda #.hibyte(brkhandler) + sta CBINV + 1 + lda IBSOUT + sta bsout + 1 + lda IBSOUT + 1 + sta bsout + 2 +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #.lobyte(dualhead) + sta IBSOUT + lda #.hibyte(dualhead) + sta IBSOUT + 1 +.endif; PLATFORM = diskio::platform::COMMODORE_128 + + LOADER_INSTALL + +.if PLATFORM = diskio::platform::COMMODORE_128 + pha + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + pla +.endif; PLATFORM = diskio::platform::COMMODORE_128 + + stx drivetype + bcc :+ +.if LOAD_VIA_KERNAL_FALLBACK + cmp #diskio::status::DEVICE_INCOMPATIBLE + beq :+ + cmp #diskio::status::TOO_MANY_DEVICES + beq :+ +.endif + jmp error + +: tya + pha + + php + sei + lda #COLOUR_BLACK + sta BGCOLOUR + sta BORDERCOLOUR + plp + +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda palntsc; as detected + cmp PALNTS; $02a6 (C-64) or $0a03 (C-128), as set by KERNAL + beq :+ + lda #ERRPALNTSC + jmp error +: +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + + DISABLE_MEMCONFIG_CHECK + + CONSOLE runningmsg + + ldx #IRQ_SLACK + lda drivetype + bpl drvconsole + + cmp #diskio::drivetype::DRIVE_GENERIC_PARALLEL + bne :+ + lda #drivemsgpa - drivemsgsl + diskio::drivetype::DRIVE_1541; generic parallel drive + ldx #KERNAL_FALLBACK_IRQ_SLACK_PARALLEL + jmp drvconsole + +: cmp #diskio::drivetype::DRIVE_GENERIC_BURST + bne :+ + lda #drivemsgbr - drivemsgsl + diskio::drivetype::DRIVE_1541; generic burst drive + ldx #KERNAL_FALLBACK_IRQ_SLACK_BURST + jmp drvconsole + +: lda #drivemsgsr - drivemsgsl + diskio::drivetype::DRIVE_1541; generic serial drive + ldx #KERNAL_FALLBACK_IRQ_SLACK_SERIAL +drvconsole: stx IRQSLACK + and #%00001111 + tax + lda drivemsgsl - diskio::drivetype::DRIVE_1541,x + ldy drivemsgsh - diskio::drivetype::DRIVE_1541,x + jsr consoleout + + CONSOLE returnmsg + + bit drivetype + bmi no2bitatn +.if PLATFORM = diskio::platform::COMMODORE_128 + bit CIA1_CRA + bvs :+; branch if IO mode is set to output + CONSOLE burstmsg + jmp no2bitatn +: +.endif; PLATFORM = diskio::platform::COMMODORE_128 + CONSOLE twobitatnm +no2bitatn: + + CONSOLE quotemsg + pla + tax + lda 0,x + ldy 1,x + ; print version string + jsr consoleout + lda #$00 + sta QTSW; leave quote mode + sta consoletime + CONSOLE quotemsg + 1 + lda #$00 + sta QTSW; leave quote mode + CONSOLE returnmsg + +.if PLATFORM = diskio::platform::COMMODORE_16 + + jsr pauseonkey; don't continue as long as any key is pressed + + CONSOLE loopmsg + + sei + jsr waitvbl + + lda #DISPLAY_DISABLE + sta TED_CTRL1 + + ldx #$00 +: lda #INTENSITY_4 | (INTENSITY_6 >> 4) + sta COLRAM + $00,x + sta COLRAM + $0100,x + sta COLRAM + $0200,x + sta COLRAM + $0300,x + lda #(COLOUR_WHITE << 4) | COLOUR_WHITE + sta COLRAM + $0400,x + sta COLRAM + $0500,x + sta COLRAM + $0600,x + sta COLRAM + $0700,x + lda #INTENSITY_6 | COLOUR_YELLOW + sta COLRAM + $0800,x + sta COLRAM + $0900,x + sta COLRAM + $0a00,x + sta COLRAM + $0b00,x + lda #' ' + sta COLRAM + $0c00,x + sta COLRAM + $0d00,x + sta COLRAM + $0e00,x + sta COLRAM + $0f00,x + inx + bne :- + txa + ldx #$06 * $08 - 1 +: sta COLRAM + $0c60,x + sta COLRAM + $0c88,x + sta COLRAM + $0d00,x; space character + sta COLRAM + $0860,x + dex + bpl :- + ldx #$11 + stx COLRAM + $0c38 + inx + stx COLRAM + $0c39 + inx + stx COLRAM + $0c3a + inx + stx COLRAM + $0c3b + inx + stx COLRAM + $0c3c + inx + stx COLRAM + $0c3d + + lda #$b0 + sta TED_CURSOR_LO + lda #$00 + sta TED_CURSOR_HI + lda #INTENSITY_0 | COLOUR_BLACK + sta TED_BGCOLOUR0 + + MEMCLEAR BITMAP, ALIGN BITMAP_SIZE, $0100 + + jsr waitvbl + + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta TED_CTRL1 + lda TED_BITMAP_ADDR + and #.lobyte(~(BITMAP_ADDR_MASK | CHARSET_BITMAP_IN_ROM)) + ora #MAKE_BITMAP_ADDR(BITMAP) + sta TED_BITMAP_ADDR + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR(COLRAM) + sta TED_SCREEN_ADDR + + sei + sec + lda #$c7 + sbc IRQSLACK + sta TED_IRQ_RASTERLINE + lda #.lobyte(~IRQ_RASTERLINE_MSB) + and TED_IMR + sta TED_IMR + lda #.lobyte(irq1) + sta CINV + lda #.hibyte(irq1) + sta CINV + 1 + cli + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + + jsr pauseonkey; don't continue as long as any key is pressed + + CONSOLE loopmsg + + sei + jsr waitvbl + + lda #DISPLAY_DISABLE + sta VIC2_CTRL1 + + ldx #$00 +: + .if LOAD_TO_UPPER_MEM + ENABLE_ALL_RAM + .endif + lda #$00 + sta SPRITES + $00,x + sta SPRITES + $0100,x + lda #(COLOUR_LIGHTGREY << 4) | COLOUR_DARKGREY + sta COLRAM + $00,x + sta COLRAM + $0100,x + sta COLRAM + $0200,x + sta COLRAM + $0300,x + .if LOAD_TO_UPPER_MEM + ENABLE_ALL_ROM + .endif + inx + bne :- + + MEMCLEAR BITMAP, ALIGN BITMAP_SIZE, $0100 + +SPRITESXPOS = $18 + + lda #SPRITESXPOS + SPRITE_WIDTH * 0 + sta SPRITE0_X + lda #SPRITESXPOS + SPRITE_WIDTH * 1 + sta SPRITE1_X + lda #SPRITESXPOS + SPRITE_WIDTH * 2 + sta SPRITE2_X + lda #SPRITESXPOS + SPRITE_WIDTH * 3 + sta SPRITE3_X + lda #SPRITESXPOS + SPRITE_WIDTH * 4 + sta SPRITE4_X + lda #SPRITESXPOS + SPRITE_WIDTH * 5 + sta SPRITE5_X + lda #SPRITESXPOS + SPRITE_WIDTH * 6 + sta SPRITE6_X + lda #SPRITESXPOS + SPRITE_WIDTH * 7 + sta SPRITE7_X + + lda #$fb + sta SPRITE0_Y + sta SPRITE1_Y + sta SPRITE2_Y + sta SPRITE3_Y + sta SPRITE4_Y + sta SPRITE5_Y + sta SPRITE6_Y + sta SPRITE7_Y + + lda #%00000000 + sta SPRITES_X_MSB + lda #%11111111 + sta VIC2_SPR_ENABLE + lda #COLOUR_YELLOW + sta VIC2_SPR0_COLOUR + sta VIC2_SPR1_COLOUR + sta VIC2_SPR2_COLOUR + sta VIC2_SPR3_COLOUR + sta VIC2_SPR4_COLOUR + sta VIC2_SPR5_COLOUR + sta VIC2_SPR6_COLOUR + sta VIC2_SPR7_COLOUR + + ldx #MAKE_SPRITE_POINTER(SPRITES) + stx SPRITESCR + SPRITE_POINTERS + inx + stx SPRITESCR + SPRITE_POINTERS + 1 + inx + stx SPRITESCR + SPRITE_POINTERS + 2 + inx + stx SPRITESCR + SPRITE_POINTERS + 3 + inx + stx SPRITESCR + SPRITE_POINTERS + 4 + inx + stx SPRITESCR + SPRITE_POINTERS + 5 + inx + stx SPRITESCR + SPRITE_POINTERS + 6 + inx + stx SPRITESCR + SPRITE_POINTERS + 7 + + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #IO_PORT_CHARGEN_DISABLED + sta IO_PORT + .endif + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta VIC2_CTRL1 + lda #VIC2_MAKE_ADDR COLRAM, BITMAP + sta VIC2_ADDR + SET_VIC_BANK VIC2_MAKE_BANK BITMAP + + .if LOAD_TO_UPPER_MEM + lda #$01 + sta VIC2_RASTERLINE + lda #.lobyte(irq0) + sta CINV + lda #.hibyte(irq0) + .else + sec + lda #$f8 + sbc IRQSLACK + sta VIC2_RASTERLINE + lda #.lobyte(irq1) + sta CINV + lda #.hibyte(irq1) + .endif + sta CINV + 1 + lda #ALL_COLUMNS + sta CIA1_PRA + lda #CIA_CLR_INTF | EVERY_IRQ + sta CIA1_ICR + bit CIA1_ICR + lda #RASTER_IRQ + sta VIC2_IMR + sta VIC2_IRR + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + + ; print the static settings + lda #.lobyte(printstatc) + sta IBSOUT + lda #.hibyte(printstatc) + sta IBSOUT + 1 + lda #0 + sta DFLTO + + cli + + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + ldx #$00 + ldy #$00 + jsr setplotxy + + ldx #.lobyte(paltext) + ldy #.hibyte(paltext) + lda palntsc + bne :+ + ldx #.lobyte(ntsctext) + ldy #.hibyte(ntsctext) +: jsr plottext + + lda #',' + jsr BSOUT + + lda #' ' + jsr BSOUT + lda #'#' +.if PLATFORM = diskio::platform::COMMODORE_128 + bit drivetype + bmi :+ + bit CIA1_CRA + bvs :+; branch if IO mode is set to output + lda #'b'; burst mode +: +.endif + jsr BSOUT + lda FA + cmp #10 + bcc :+ + lda #'1' + jsr BSOUT + sec + lda FA + sbc #10 +: ora #'0' + jsr BSOUT + lda #'/' + jsr BSOUT + + lda drivetype + and #%00001111 + tax + lda drivtypesl - diskio::drivetype::DRIVE_1541,x + ldy drivtypesh - diskio::drivetype::DRIVE_1541,x + tax + jsr plottext + + lda #',' + jsr BSOUT + lda #' ' + jsr BSOUT + lda #'$' + jsr BSOUT + + lda #.hibyte(__DISKIO_SIZE__) + beq :+ + jsr plotbyte +: lda #.lobyte(__DISKIO_SIZE__) + jsr plothex + + POP_MEMCONFIG + +.if !INSTALL_ONLY + +.if PLATFORM = diskio::platform::COMMODORE_16 + sta TED_RAM_ENABLE + lda #.lobyte(~FORCE_SINGLE_CLOCK) + sei + and TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + cli +.elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + sta MMU_CR +.else + lda #MEMCONFIG_IO + sta IO_PORT +.endif + + ENABLE_MEMCONFIG_CHECK + +.if NO_SPRITES_OR_IRQ + .if PLATFORM <> diskio::platform::COMMODORE_16 + lda #$00 + sta VIC2_IMR + sei + sta VIC2_SPR_ENABLE + .else + lda #$00 + sta TED_IMR + lda TED_IRR + sta TED_IRR + sei + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta TED_CTRL1 + .endif +.endif + +testloop: +.if !NO_SPRITES_OR_IRQ + DISABLE_MEMCONFIG_CHECK + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + + CONSOLE looprunmsg +looprunhi = * + 1 + lda #0 +looprunlo = * + 1 + ldx #1 + jsr printword + + .if USE_2_MHZ + ldx #'1' + lda VIC2_C128_CLOCK + cmp #.lobyte(~C128_INC_LINECNTR); $fd, will say "1 MHz" for Turbo Chameleon turbo mode ($ff, as on C-64) + bne :+ + inx +: stx mhz + CONSOLE mhzmsg + .endif + CONSOLE returnmsg + + lda #.lobyte(printstatc) + sta IBSOUT + lda #.hibyte(printstatc) + sta IBSOUT + 1 + + inc looprunlo + bne :+ + inc looprunhi +: + POP_MEMCONFIG + ENABLE_MEMCONFIG_CHECK +.endif + +.if LOAD_RAW_API + ; --- load raw --- + + .macro LOAD_RAW filename + .local done + + INITSTAT + + .if LOAD_TO_API + LOADRAW_LOADTO filename, BITMAP + $20 + .else + LOADRAW filename + .endif + jcs error + + PRINTSTAT BITMAP_SIZE + 2 +done: + .endmacro + + .if LOAD_TO_API + TESTUNCOMPRESSED LOAD_RAW, "LoadRaw", $20 + .else + TESTUNCOMPRESSED LOAD_RAW, "LoadRaw", 0 + .endif + + .if TEST_INVALID_PARAMETERS + PRINTTESTNAME "LoadRawInv" + .if !NO_SPRITES_OR_IRQ + lda #.lobyte(bogusname) + sta filenamelo + lda #.hibyte(bogusname) + sta filenamehi + .endif + INITSTAT + MEMCONFIG_BUFFER + + LOADRAW bogusname + jcc error + cmp #diskio::status::FILE_NOT_FOUND + jne error + + PRINTSTAT 0 + MEMCONFIG_CHECK + .endif +.endif + +.if FILE_EXISTS_API + PRINTTESTNAME "FileExists" + + lda #.lobyte(pic1unc) + sta filenamelo + lda #.hibyte(pic1unc) + sta filenamehi + INITSTAT + + FILEEXISTS pic1unc + jcs error + + PRINTSTAT 0 + + lda #.lobyte(bogusname) + sta filenamelo + lda #.hibyte(bogusname) + sta filenamehi + INITSTAT + + FILEEXISTS bogusname + jcc error + cmp #diskio::status::FILE_NOT_FOUND + jne error + + PRINTSTAT 0 + + jsr checkkeys +.endif + +.if TEST_SAVE + PRINTTESTNAME "Save" + + lda #.lobyte(pic1unc) + sta filenamelo + lda #.hibyte(pic1unc) + sta filenamehi + INITSTAT + + SAVE_OVERWRITE saveparams + bcc donesave + jmp error + +saveparams: .word pic1unc; filename + .word BITMAP; from + .word BITMAP_SIZE; length + .word BITMAP; loadaddress + .word __SWAPBUFFER_LOAD__; buffer + +donesave: PRINTSTAT BITMAP_SIZE + 2 + + jsr checkkeys +.endif; TEST_SAVE + +.if MEM_DECOMP_API + + ; --- load a compressed file, then decompress after loading + + .macro LOADANDMEMDECOMP_COMMON filename + .local done + + INITSTAT + + .if LOAD_RAW_API + LOADRAW filename + jcs error + .else + .error "To test memory decompression, please set LOAD_RAW_API to non-0" + .endif + + clc +done: + .endmacro + + .macro LOADANDMEMDECOMP filename + .local done + + .if VERIFY + .local verifyok + + MEMCLEAR BITMAP, ALIGN BITMAP_SIZE, $0100 + + LOADANDMEMDECOMP_COMMON filename + + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + MEMCOPY BITMAP + ALIGN (BITMAP_SIZE >> 1), $0100, COMPDATA + ALIGN BITMAP_SIZE, $0100, ALIGN BITMAP_SIZE, $0100 + POP_MEMCONFIG + MEMCLEAR BITMAP, ALIGN BITMAP_SIZE, $0100 + + LOADANDMEMDECOMP_COMMON filename + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + + MEMCOMP BITMAP + ALIGN (BITMAP_SIZE >> 1), $0100, COMPDATA + ALIGN BITMAP_SIZE, $0100, ALIGN BITMAP_SIZE, $0100 + + php + pla + tax + POP_MEMCONFIG + txa + pha + plp + beq verifyok + jsr verifyfail +verifyok: + .else + LOADANDMEMDECOMP_COMMON filename + .endif + + .if LOAD_TO_UPPER_MEM + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + .endif + + lda loadaddrlo + sta POINTERS + lda loadaddrhi + sta POINTERS + 1 + dec loadaddrlo + inc loadaddrhi + + .if MEM_DECOMP_TO_API + MEMDECOMP_TO POINTERS, POINTERS + 1, BITMAP - $10 + .else + MEMDECOMP POINTERS, POINTERS + 1 + .endif + + .if LOAD_TO_UPPER_MEM + POP_MEMCONFIG + .endif + + PRINTSTAT BITMAP_SIZE + 2 +done: + .endmacro + + TESTCOMPRESSED LOADANDMEMDECOMP, "MemDecomp", 0 +.endif + +.if LOAD_COMPD_API + + ; --- load with decompression --- + + .macro LOADCOMPRESSED filename + .local done + + INITSTAT + + .if LOAD_TO_API + LOADCOMPD_RELTO filename, -$10 + .else + LOADCOMPD filename + .endif + jcs error + + PRINTSTAT BITMAP_SIZE + 2 +done: + .endmacro + + .if LOAD_TO_API + TESTCOMPRESSED LOADCOMPRESSED, "LoadCompd", -$10 + .else + TESTCOMPRESSED LOADCOMPRESSED, "LoadCompd", 0 + .endif + + .if TEST_INVALID_PARAMETERS + PRINTTESTNAME "LoadCompdInv" + .if !NO_SPRITES_OR_IRQ + lda #.lobyte(bogusname) + sta filenamelo + lda #.hibyte(bogusname) + sta filenamehi + .endif + INITSTAT + MEMCONFIG_BUFFER + + LOADCOMPD bogusname + jcc error + cmp #diskio::status::FILE_NOT_FOUND + jne error + + PRINTSTAT 0 +loadcmpdun: MEMCONFIG_CHECK + .endif +.endif; LOAD_COMPD_API + + inc bordercol + 1 +setborder: lda #1 + beq :+ +bordercol: lda #0 + sta BORDERCOLOUR +: + .if USE_2_MHZ + lda #C128_TWO_MHZ + eor VIC2_C128_CLOCK + sta VIC2_C128_CLOCK + .endif + jmp testloop + +.if PLATFORM = diskio::platform::COMMODORE_16 + +pauseonkey: lda #ALL_COLUMNS +: sei + sta PIO_KEYBOARD_COLUMN + sta TED_KEYBOARD_LATCH + ldx TED_KEYBOARD_LATCH + sta TED_KEYBOARD_LATCH + cpx TED_KEYBOARD_LATCH + cli + bne :- + cpx #NO_ROWS + beq :+; branch if no key presed + ; pause on any key except left shift (lock) + ldx #LEFT_SHIFT_COLUMN + ldy #LEFT_SHIFT_ROW + sei + stx PIO_KEYBOARD_COLUMN + stx TED_KEYBOARD_LATCH + cpy TED_KEYBOARD_LATCH + cli + bne pauseonkey +: rts + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + +pauseonkey: lda #ALL_COLUMNS +: sei + sta CIA1_PRA + ldx CIA1_PRB + cpx CIA1_PRB + cli + bne :- + inx + beq :+; branch if no key presed + ; pause on any key except left shift (lock) + lda #LEFT_SHIFT_COLUMN + ldx #LEFT_SHIFT_ROW + sei + sta CIA1_PRA + cpx CIA1_PRB + cli + bne pauseonkey +: rts + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + +checkkeys: +.if PAUSE_BETWEEN_LOADS + lda #PAUSE_BETWEEN_LOADS + ldx #$00 + ldy #$00 +: dex + bne :- + dey + bne :- + sec + sbc #$01 + bne :- +.endif + +.if !UNINSTALL_API + jmp pauseonkey +.else + + .if PLATFORM = diskio::platform::COMMODORE_16 + lda #ALL_COLUMNS + sta PIO_KEYBOARD_COLUMN + sta TED_KEYBOARD_LATCH + lda TED_KEYBOARD_LATCH + cmp #NO_ROWS + .else + inc CIA1_PRB; uninstall on keypress + .endif + bne off + rts + +off: + LOADER_UNINSTALL +.endif; !UNINSTALL_API + +.else ; INSTALL_ONLY + .if UNINSTALL_API +: + .if PLATFORM = diskio::platform::COMMODORE_16 + lda #ALL_COLUMNS + sta PIO_KEYBOARD_COLUMN + sta TED_KEYBOARD_LATCH + lda TED_KEYBOARD_LATCH + cmp #NO_ROWS + .else + inc CIA1_PRB; uninstall on keypress + .endif + beq :- + LOADER_UNINSTALL + .endif; UNINSTALL_API +.endif; INSTALL_ONLY + + DISABLE_MEMCONFIG_CHECK + +.if UNINSTALL_API + lda #MSGUNINST + jsr printmsg + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + CONSOLE cmsguninst + + .if 0 + sei + lda #.lobyte(KEY) + sta CINV + lda #.hibyte(KEY) + sta CINV + 1 + + lda #0 + sta VIC2_IMR + sta VIC2_IRR + sta VIC2_SPR_ENABLE + dec BORDERCOLOUR + + lda #savefilend - savefile + Ldx #.lobyte(savefile) + ldy #.hibyte(savefile) + jsr SETNAM + lda #0 + ldx FA + tay + jsr SETLFS + + lda #.lobyte(savefile) + sta STAL + lda #.hibyte(savefile) + sta STAH + + lda #STAL + ldx #.lobyte(savefilend) + ldy #.hibyte(savefilend) + jsr SAVE + bcc :+ + jmp error + +savefile: .byte "savefile" +savefilend: + +: + .endif +.endif + jsr waitvbl + lda #COLOUR_GREEN + sta bordercol + 1 + sta BORDERCOLOUR + +halt: jmp halt + +waitvbl: +.if PLATFORM <> diskio::platform::COMMODORE_16 + lda palntsc + cmp #$01 +.endif + WAIT_VBL + rts + +initstat: +.if !NO_SPRITES_OR_IRQ + DISABLE_MEMCONFIG_CHECK + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + + CONSOLE logmsg + +testnamelo = * + 1 + lda #$00 +testnamehi = * + 1 + ldy #$00 + jsr consoleout + CONSOLE spacemsg + lda filenamehi + beq noname + CONSOLE quotemsg +filenamelo = * + 1 + lda #$00 + sta POINTERS +filenamehi = * + 1 + lda #$00 + sta POINTERS + 1 + ldy #0 +: lda (POINTERS),y + beq :++ + cmp #$40 + bcc :+ + eor #$80 +: sta capsname,y + iny + bne :-- +: sta capsname,y + lda #.lobyte(capsname) + ldy #.hibyte(capsname) + jsr consoleout + CONSOLE quotemsg + 1 + +noname: lda #.lobyte(printstatc) + sta IBSOUT + lda #.hibyte(printstatc) + sta IBSOUT + 1 + + POP_MEMCONFIG + ENABLE_MEMCONFIG_CHECK +.endif + +.if PLATFORM = diskio::platform::COMMODORE_16 +calibinits: + .if COUPLED_TIMERS + ldx #$ff + + lda TED_CHARGEN_ADDR + and #FORCE_SINGLE_CLOCK + pha + bne :+ + lda #FORCE_SINGLE_CLOCK + php + sei + ora TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp + +: stx TED_COUNTER1_LO + stx TED_COUNTER1_HI + stx TED_COUNTER2_LO + stx TED_COUNTER2_HI + + pla + bne :+ + lda #.lobyte(~FORCE_SINGLE_CLOCK) + php + sei + and TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp +: + .else + jsr readcycles + sta prvcycslo + stx prvcycsmid + sty prvcycshi + .endif +.else + lda #$ff + sta CIA2_TA_LO + sta CIA2_TA_HI + sta CIA2_TB_LO + sta CIA2_TB_HI + lda #FORCE_LOAD | CONTINUOUS | COUNT_TA_UNDF | TIMER_START + sta CIA2_CRB + lda CIA2_CRA + and #.lobyte(~(COUNT_CNT | ONE_SHOT)) + ora #FORCE_LOAD | CONTINUOUS | TIMER_START + sta CIA2_CRA +.endif + rts + + ; in: x/y - number of bytes transferred +printstat: +.if PLATFORM = diskio::platform::COMMODORE_16 + + stx statxbuf + sty statybuf + + .if COUPLED_TIMERS + lda TED_CHARGEN_ADDR + and #FORCE_SINGLE_CLOCK + pha + bne :+ + lda #FORCE_SINGLE_CLOCK + php + sei + ora TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp + +: ldy TED_COUNTER1_HI + ldx TED_COUNTER2_HI + lda TED_COUNTER1_LO + sta :+ + 1 + lda TED_COUNTER2_LO + cpy TED_COUNTER1_HI + bne :- + cpx TED_COUNTER2_HI + bne :- + pha; TED_COUNTER2_LO + eor #$ff + sbc #70; empirically found offset + sta numcycles + $00 + txa; TED_COUNTER2_HI + eor #$ff + sbc #$00 + sta numcycles + $01 + pla; TED_COUNTER2_LO +: sbc #$00; TED_COUNTER1_LO + sta numcycles + $02 + lda #$00 + sta numcycles + $03 + + pla + bne :+ + lda #.lobyte(~FORCE_SINGLE_CLOCK) + php + sei + and TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp +: + .else + .if NO_SPRITES_OR_IRQ + ; no speed measurement without IRQs nor coupled timers + rts + .endif + + jsr diffcycles + .endif + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + + lda #.lobyte(~TIMER_START) + and CIA2_CRA + sta CIA2_CRA + lda #TIMER_STOP + sta CIA2_CRB + + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + sec + lda #$ff - 31; subtract overhead + sbc CIA2_TA_LO + sta numcycles + $00 + lda #$ff + sbc CIA2_TA_HI + sta numcycles + $01 + lda #$ff + sbc CIA2_TB_LO + sta numcycles + $02 + lda #$ff + sbc CIA2_TB_HI + sta numcycles + $03 + + stx statxbuf + sty statybuf + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + + ; print kB/s figure + lda #0 + sta DFLTO + +.if NO_SPRITES_OR_IRQ + lda #.lobyte(printstatb) + sta IBSOUT + lda #.hibyte(printstatb) + sta IBSOUT + 1 + + lda #.lobyte(BITMAP + (8 * (SCREEN_ROWS - 1) * SCREEN_COLUMNS)) + pha + lda #.hibyte(BITMAP + (8 * (SCREEN_ROWS - 1) * SCREEN_COLUMNS)) + pha +.else + ldx #$00 + ldy #$01 + jsr setplotxy + ; buffer pointer to stats + lda POINTERS + $02 + pha + lda POINTERS + $03 + pha +.endif + +statxbuf = * + $01 + lda #$00; number of bytes transferred +statybuf = * + $01 + ldy #$00 + jsr GIVAYF; fac#1 = numbytes + + lda #.lobyte(cyclesperd) + ldy #.hibyte(cyclesperd) + jsr FMULT; fac#1 *= cycles per 1/10 seconds + jsr MOVAF; fac#2 = fac#1 + + lda numcycles + $00 + ora numcycles + $01 + ora numcycles + $02 + ora numcycles + $03 + bne :+ + inc numcycles + $00 +: INT32TOFAC numcycles; fac#1 = numcycles + + jsr FDIVT; fac#1 = fac#2 / numcycles + + jsr FADDH; fac#1 += 0.5 + jsr MOVAF; fac#2 = fac#1 + + ldx #$00 + lda #.lobyte(hundred) + ldy #.hibyte(hundred) + jsr FDIVM; fac#1 = fac#2 / 100 + + ; fac#1 = cyclespersecond * numbytes / numcycles / 1000 + jsr FOUT + + ; truncate to 2 decimals (floored rounding) + ldy #$00 +: lda FBUFFR + 1,y + beq :+ + iny + cmp #'.' + bne :- + iny + iny +: lda #0 + sta FBUFFR + 1,y +.if !NO_SPRITES_OR_IRQ + sty statlen +.endif + ; restore pointer to sprite bitmap + pla + sta POINTERS + 3 + pla + sta POINTERS + 2 + + ldx #TESTNAMEPOS; num chars + lda statxbuf + ora statybuf + beq :+ + + ldx #.lobyte(FBUFFR + 1) + ldy #.hibyte(FBUFFR + 1) + jsr plottext + + ldx #.lobyte(kbstext) + ldy #.hibyte(kbstext) + jsr plottext + +.if NO_SPRITES_OR_IRQ +: +.else; NO_SPRITES_OR_IRQ + sec + lda #TESTNAMEPOS; num chars +statlen = * + 1 + sbc #0 + sbc #5; length of " kB/s" + tax + beq :++ + bmi :++ +: lda #' ' + jsr BSOUT + dex + bne :- +: + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + + lda statxbuf + ora statybuf + beq nokbscons + + CONSOLE kbsspcmsg + CONSOLE FBUFFR + 1 + CONSOLE kbscoltext +.if PLATFORM = diskio::platform::COMMODORE_64 + lda PNTR + beq :+ + cmp #SCREEN_COLUMNS + bne :++ +: inc consoletime + jmp nonewline +: +.endif +nokbscons: CONSOLE returnmsg +nonewline: +.endif; NO_SPRITES_OR_IRQ + + lda #.lobyte(printstatc) + sta IBSOUT + lda #.hibyte(printstatc) + sta IBSOUT + 1 + + POP_MEMCONFIG + rts + +error: sei + sta errora + 1 + stx errorx + 1 + sty errory + 1 + DISABLE_MEMCONFIG_CHECK +.if PLATFORM = diskio::platform::COMMODORE_16 + sta TED_ROM_ENABLE +.elseif PLATFORM <> diskio::platform::COMMODORE_128 + lda #MEMCONFIG_IO_KERNAL + sta IO_PORT +.endif; PLATFORM <> diskio::platform::COMMODORE_128 + tsx + dex + dex + txs + pla + sec + sbc #$02 + sta erroraddrl + 1 + pla + cli + sbc #$00 + sta erroraddrh + 1 + +errora: lda #$00 +errorx: ldx #$00 +errory: ldy #$00 + ; print error message + lsr putchar + 1 + jsr prntmsgadr + lda bsout + 1 + sta IBSOUT + lda bsout + 2 + sta IBSOUT + 1 + jsr timestamp + lda #PETSCII_RED + jsr BSOUT + sec + ror putchar + 1 + lda errora + 1 + ldx errorx + 1 + ldy errory + 1 + jsr prntmsgadr +.if PLATFORM = diskio::platform::COMMODORE_128 + jsr SWAPPER + jsr timestamp + lda #PETSCII_RED + jsr BSOUT + sec + ror putchar + 1 + lda errora + 1 + ldx errorx + 1 + ldy errory + 1 + jsr prntmsgadr +.endif; PLATFORM = diskio::platform::COMMODORE_128 + +errorhalt: jsr waitvbl + php + sei + lda #COLOUR_RED + sta BORDERCOLOUR + sta bordercol + 1 + plp + lda #.lobyte(~MULTICOLOUR_MODE) +.if PLATFORM = diskio::platform::COMMODORE_16 + and TED_CTRL2 + sta TED_CTRL2 +.else + and VIC2_CTRL2 + sta VIC2_CTRL2 +.endif +errorhaltl: ldy #12 +: jsr waitvbl + dey + bne :- + lda #COLOUR_RED ^ COLOUR_BLACK + eor BORDERCOLOUR + php + sei + sta BORDERCOLOUR + sta bordercol + 1 + plp +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #VRT +: bit VDC_SR + beq :- + lda #FG_BG + sta VDC_CR + lda #RED +: bit VDC_SR; wait until update ready + bpl :- + eor VDC_DR + sta VDC_DR +.endif; PLATFORM = diskio::platform::COMMODORE_128 + jmp errorhaltl + +.if PLATFORM = diskio::platform::COMMODORE_128 +: jsr bsout + ldx dualheadx + 1 + rts + +dualhead: stx dualheadx + 1 + ldx DFLTO + cpx #8 + bcs :- + sta :+ + 1 + jsr bsout + jsr SWAPPER +: lda #0 + jsr bsout + jsr SWAPPER +dualheadx: ldx #0 + rts +.endif; PLATFORM = diskio::platform::COMMODORE_128 + +consoleout: +.if PLATFORM = diskio::platform::COMMODORE_128 + tax + pha + tya + pha + lda consoletime + pha + txa + jsr doconsout + jsr SWAPPER + pla + sta consoletime + pla + tay + pla + jsr doconsout + jmp SWAPPER +doconsout: +.endif; PLATFORM = diskio::platform::COMMODORE_128 + + sta POINTERS + $00 + sty POINTERS + $01 + + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + + lda #DEVICE_SCREEN + sta DFLTO + lda #$00 + sta QTSW; disable quote mode +consoletime = * + $01 + lda #$00 + beq notimestmp; don't print timestamp if 0 + lda #$00 + sta consoletime + jsr timestamp +notimestmp: + +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR +.elseif PLATFORM <> diskio::platform::COMMODORE_16 + lda #MEMCONFIG_IO_KERNAL + sta IO_PORT +.endif + ldy #$00 +: + lda (POINTERS + $00),y + beq :++ + pha + jsr BSOUT + pla + cmp #PETSCII_RETURN + bne :+ + inc consoletime +: iny + bne :-- +: + POP_MEMCONFIG + rts + +timestamp: lda #$00 + sta QTSW; disable quote mode + lda PNTR + beq :+ +.if PLATFORM = diskio::platform::COMMODORE_64 + cmp #SCREEN_COLUMNS + beq :+ +.endif + lda #PETSCII_RETURN + jsr BSOUT +: lda #PETSCII_LIGHTBLUE + jsr BSOUT +.if PLATFORM = diskio::platform::COMMODORE_16 + lda tod_hrs +.else + lda #%00011111 + and CIA1_TOD_HRS +prevhours: cmp #$00 + sta prevhours + 1 + bcs :+ + lda #$12 + php + sei + sed + adc hoursbase + 1 + plp + sta hoursbase + 1 + lda prevhours + 1 +: clc + php + sei + sed +hoursbase: adc #$00 + plp + cmp #0 +.endif + beq :+ + jsr putconbyte + lda #':' + jsr BSOUT +: +.if PLATFORM = diskio::platform::COMMODORE_16 + lda tod_mins +.else + lda CIA1_TOD_MIN +.endif + jsr putconbyte + lda #':' + jsr BSOUT +.if PLATFORM = diskio::platform::COMMODORE_16 + lda tod_secs +.else + lda CIA1_TOD_SEC +.endif + jsr putconbyte +.if PLATFORM <> diskio::platform::COMMODORE_16 + bit CIA1_TOD_10S; to free the latch +.endif + lda #'.' + jsr BSOUT +.if PLATFORM = diskio::platform::COMMODORE_16 + lda tod_10s +.else + lda CIA1_TOD_10S +.endif + jsr putconibbl + lda #' ' + jmp BSOUT + +putconword: txa + pha + tya + jsr putconbyte + pla + +putconbyte: tax + lsr + lsr + lsr + lsr + cmp #10 + bcc :+ + adc #'a' - '0' - 10 - 1 +: adc #'0' + jsr BSOUT + txa +putconibbl: and #%00001111 + cmp #10 + bcc :+ + adc #'a' - '0' - 10 - 1 +: adc #'0' + jmp BSOUT + +prntmsgadr: cmp #ERRBRKOPC + beq :+ + sec; with address + SKIPBYTE + +printmsg: clc + sta printa +: stx printx + sty printy + + tax + lda errormsgsl,x + sta POINTERS + $00 + lda errormsgsh,x + sta POINTERS + $01 + + lda putchar + 1 + bmi :+ + + lda #.lobyte(BITMAP) + sta POINTERS + $02 + lda #.hibyte(BITMAP) + sta POINTERS + $03 + lda #$00 + tay +clearline: sta BITMAP + $00,y + sta BITMAP + $40,y + iny + bne clearline + + lda #1 +: sta putchar + 1 + tay + bne :+ +.if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR($0800) + sta TED_SCREEN_ADDR + lda #.hibyte($0c00) +.else + lda #VIC2_MAKE_ADDR($0400, CHARSET_ADDR_UPPERLOWER) + sta VIC2_ADDR + lda #.hibyte($0400) +.endif + sta POINTERS + $03 + +: +.if PLATFORM = diskio::platform::COMMODORE_16 + PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM +.elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | RAM_ROM + bit putchar + 1 + bpl :+ + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE +: sta MMU_CR +.else; PLATFORM <> diskio::platform::COMMODORE_128 + lda #IO_PORT_DIRECTION_DEFAULT + sta IO_PORT_DIRECTION + lda #MEMCONFIG_IO_KERNAL + sta IO_PORT +.endif; PLATFORM <> diskio::platform::COMMODORE_128 + + txa + pha + eor #MSGUNINST + beq doprintmsg + bcc :+ + lda #'@' + jsr putchar +erroraddrh: lda #$00 + jsr puthex +erroraddrl: lda #$00 + jsr puthexnum + lda #' ' + jsr putchar +: + lda #'A' + jsr putchar + lda #'=' + jsr putchar +printa = * + 1 + lda #0 + jsr puthex + + lda #' ' + jsr putchar + lda #'X' + jsr putchar + lda #'=' + jsr putchar +printx = * + 1 + lda #$00 + jsr puthex + lda #' ' + jsr putchar + lda #'Y' + jsr putchar + lda #'=' + jsr putchar +printy = * + 1 + lda #$00 + jsr puthex + lda #':' + jsr putchar + lda #' ' + jsr putchar + + lda #$00 + sta doprintmsg + 1 +doprintmsg: ldy #$00 + lda (POINTERS + $00),y + beq :+ + jsr putchar + inc doprintmsg + 1 + bne doprintmsg +: pla + cmp #ERRBRKOPC + bne :+ + +brkaddrhi = * + $01 + lda #$00 + jsr puthexnum +brkaddrlo = * + $01 + lda #$00 + jsr puthexnum + lda #'.' + jsr putchar +: +.if PLATFORM = diskio::platform::COMMODORE_16 + POP_MEMCONFIG +.elseif PLATFORM = diskio::platform::COMMODORE_128 + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR +.else; PLATFORM <> diskio::platform::COMMODORE_128 + lda #MEMCONFIG_IO_KERNAL + sta IO_PORT +.endif; PLATFORM <> diskio::platform::COMMODORE_128 + rts + +tohex: tay + lsr + lsr + lsr + lsr + cmp #10 + bcc :+ + adc #.lobyte('a' - '0' + $80 - 10 - 1) +: adc #'0' + tax + tya + and #%00001111 + cmp #10 + bcc :+ + adc #.lobyte('a' - '0' + $80 - 10 - 1) +: adc #'0' + rts + +puthex: tax + lda #'$' + jsr putchar + txa +puthexnum: jsr tohex + pha + txa + jsr putchar + pla + +putchar: ldy #$00 + bpl :+ + jmp bsout + +: jsr petsciiscr + ldy putchar + 1 + bne plotbitmap + + ; print char to screen + sta (POINTERS + $02),y + lda POINTERS + $03 + pha + and #%00000011 +.if PLATFORM = diskio::platform::COMMODORE_16 + ora #.hibyte($0800) + sta POINTERS + $03 + lda #COLOUR_WHITE | INTENSITY_7 + sta (POINTERS + $02),y +.elseif PLATFORM = diskio::platform::COMMODORE_128 + ora #.hibyte(VIC2_COLOURRAM) + sta POINTERS + $03 + lda MMU_CR + pha + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR + lda #COLOUR_WHITE + sta (POINTERS + $02),y + pla + sta MMU_CR +.else; PLATFORM <> diskio::platform::COMMODORE_128 + ora #.hibyte(VIC2_COLOURRAM) + sta POINTERS + $03 + lda IO_PORT + pha + lda #MEMCONFIG_IO + sta IO_PORT + lda #COLOUR_WHITE + sta (POINTERS + $02),y + pla + sta IO_PORT +.endif; PLATFORM <> diskio::platform::COMMODORE_128 + + pla + sta POINTERS + $03 + inc POINTERS + $02 + rts + + ; plot char to bitmap +plotbitmap: +.if PLATFORM = diskio::platform::COMMODORE_64 + tay + lda IO_PORT + pha + lda #MEMCONFIG_CHARGEN + sta IO_PORT + tya +.endif + ldy #$00 + sty POINTERS + $05 + asl + rol POINTERS + $05 + asl + rol POINTERS + $05 + asl + rol POINTERS + $05 + sta POINTERS + $04 + lda #.hibyte(ROM_CHARSET_UPLOW) + adc POINTERS + $05 + sta POINTERS + $05 + ldy #$07 +: lda (POINTERS + $04),y + sta (POINTERS + $02),y + dey + bpl :- + clc + lda #$08 + adc POINTERS + $02 + sta POINTERS + $02 + bcc :+ + inc POINTERS + $03 +: +.if PLATFORM = diskio::platform::COMMODORE_64 + pla + sta IO_PORT +.endif + rts + + ; in: a - length in pages + ; x - destination hibyte +memclear: stx POINTERS + $01 + ldy #$00 + sty POINTERS + $00 + tax + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM + tya +memclrlp: sta (POINTERS + $00),y + iny + bne memclrlp + inc POINTERS + $01 + dex + bne memclrlp + POP_MEMCONFIG + rts + + ; in: a - length in pages + ; x - source hibyte + ; y - destination hibyte +memcopy: stx POINTERS + $01 + sty POINTERS + $03 + ldy #$00 + sty POINTERS + $00 + sty POINTERS + $02 + tax +memcpylp: lda (POINTERS + $00),y + sta (POINTERS + $02),y + iny + bne memcpylp + inc POINTERS + $01 + inc POINTERS + $03 + dex + bne memcpylp + rts + + .import __SWAPBUFFER_LOAD__ + + .pushseg + .segment "SWAPBUFFER" + .popseg + +.if VERIFY + +COMPDATA = __SWAPBUFFER_LOAD__ +COMPBMP = COMPDATA + +verifyfail: tya + jsr tohex + sta verifysrc + $03 + stx verifysrc + $02 + sta verifydest + $03 + stx verifydest + $02 + lda POINTERS + $02 + jsr tohex + sta verifysrc + $01 + stx verifysrc + $00 + lda POINTERS + $04 + jsr tohex + sta verifydest + $01 + stx verifydest + $00 + pla + pla + lda #MSGVERIFAIL + jmp error + +piccopy: +.if LOAD_TO_UPPER_MEM + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM +.endif + MEMCOPY BITMAP, COMPBMP, ALIGN BITMAP_SIZE, $0100 +.if LOAD_TO_UPPER_MEM + POP_MEMCONFIG +.endif + rts + +piccomp: +.if LOAD_TO_UPPER_MEM + PUSH_MEMCONFIG_AND_ENABLE_ALL_RAM +.endif + MEMCOMP BITMAP, COMPBMP, ALIGN BITMAP_SIZE, $0100 +.if LOAD_TO_UPPER_MEM + php + pla + tax + POP_MEMCONFIG + txa + pha + plp +.endif + rts + + ; in: POINTERS + $00 - lobyte of length + ; a - hibyte of length + ; x - memory area 1 hibyte + ; y - memory area 2 hibyte +memcomp: stx POINTERS + $02 + sty POINTERS + $04 + ldy #$00 + sty POINTERS + $01 + sty POINTERS + $03 + tax +memcmplp: lda (POINTERS + $01),y + cmp (POINTERS + $03),y + bne memcmpne + iny + bne memcmplp + inc POINTERS + $02 + inc POINTERS + $04 + dex + bne memcmplp + lda POINTERS + $00 + beq memcmpne +memcmpl1: lda (POINTERS + $01),y + cmp (POINTERS + $03),y + bne memcmpne + iny + cpy POINTERS + $00 + bne memcmpl1 +memcmpne: rts + +.endif; VERIFY + +plothex: pha + lsr + lsr + lsr + lsr + cmp #10 + bcc :+ + adc #'a' - '0' - 10 - 1 +: adc #'0' + jsr BSOUT + pla +plotbyte: and #%00001111 + cmp #10 + bcc :+ + adc #'a' - '0' - 10 - 1 +: adc #'0' + jmp BSOUT + +plottext: PUSH_MEMCONFIG_AND_ENABLE_ALL_ROM + stx POINTERS + $00 + sty POINTERS + $01 + + ldy #$00 +plotloop: lda (POINTERS + $00),y + beq plotend + jsr petsciiscr + cmp #$1f; scrcode "_" fix + bne :+ + lda #$64 +: jsr BSOUT + iny + bne plotloop + +plotend: POP_MEMCONFIG + rts + +petsciiscr: ; convert to screen code + cmp #$60 + bcc :+ + and #%01011111 + SKIPWORD +: and #%00111111 + rts + +printword: +.if PLATFORM = diskio::platform::COMMODORE_128 + pha + ENABLE_ALL_ROM + pla + tay + txa + pha + tya + pha +.endif; PLATFORM = diskio::platform::COMMODORE_128 + jsr LINPRT +.if PLATFORM = diskio::platform::COMMODORE_128 + jsr SWAPPER + pla + tay + pla + tax + tya + jsr LINPRT + jsr SWAPPER + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | IO_SPACE + sta MMU_CR +.endif; PLATFORM = diskio::platform::COMMODORE_128 + rts + +.if PLATFORM = diskio::platform::COMMODORE_16 + +setplotxy: txa + pha + lda #.lobyte(COLRAM + $0fe8) + ldx #.hibyte(COLRAM + $0fe8) + cpy #$00 + beq :+ + ; second stats char line + lda #.lobyte(COLRAM + $0c10) + ldx #.hibyte(COLRAM + $0c10) +: sta POINTERS + $02 + stx POINTERS + $03 + pla + clc + adc POINTERS + $02 + sta POINTERS + $02 + bcc :+ + inc POINTERS + $03 +: rts + +.if NO_SPRITES_OR_IRQ +printstatb: stx printxbuf + sty printybuf + jsr plotbitmap +printxbuf = * + $01 + ldx #$00 + jmp printstatr +.endif + +printstatc: sty printybuf + pha + lda DFLTO + beq :+ + pla +bsout: jmp $00 +: pla + ldy #$00 + sta (POINTERS + $02),y + inc POINTERS + $02 + bne :+ + inc POINTERS + $03 +: +printstatr: +printybuf = * + $01 + ldy #$00 + clc + rts + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + +setplotxy: clc + lda plotxposlo,x + adc plotypos,y + sta POINTERS + $02 + lda plotxposhi,x + sta POINTERS + $03 + rts + +plotxposlo: .lobytes SPRITES + $0000, SPRITES + $0001, SPRITES + $0002 + .lobytes SPRITES + $0040, SPRITES + $0041, SPRITES + $0042 + .lobytes SPRITES + $0080, SPRITES + $0081, SPRITES + $0082 + .lobytes SPRITES + $00c0, SPRITES + $00c1, SPRITES + $00c2 + .lobytes SPRITES + $0100, SPRITES + $0101, SPRITES + $0102 + .lobytes SPRITES + $0140, SPRITES + $0141, SPRITES + $0142 + .lobytes SPRITES + $0180, SPRITES + $0181, SPRITES + $0182 + .lobytes SPRITES + $01c0, SPRITES + $01c1, SPRITES + $01c2 +plotxposhi: .hibytes SPRITES + $0000, SPRITES + $0001, SPRITES + $0002 + .hibytes SPRITES + $0040, SPRITES + $0041, SPRITES + $0042 + .hibytes SPRITES + $0080, SPRITES + $0081, SPRITES + $0082 + .hibytes SPRITES + $00c0, SPRITES + $00c1, SPRITES + $00c2 + .hibytes SPRITES + $0100, SPRITES + $0101, SPRITES + $0102 + .hibytes SPRITES + $0140, SPRITES + $0141, SPRITES + $0142 + .hibytes SPRITES + $0180, SPRITES + $0181, SPRITES + $0182 + .hibytes SPRITES + $01c0, SPRITES + $01c1, SPRITES + $01c2 +plotypos: .byte $00 * $03, $08 * $03, $10 * $03 + +.if NO_SPRITES_OR_IRQ +printstatb: sta POINTERS + $05 + txa + pha + tya + pha + .if PLATFORM = diskio::platform::COMMODORE_128 + lda MMU_CR + pha + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | RAM_ROM + sta MMU_CR + .else; PLATFORM <> diskio::platform::COMMODORE_128 + lda IO_PORT + pha + lda #MEMCONFIG_CHARGEN + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + lda POINTERS + $05 + jsr plotbitmap + jmp printstatr +.endif + +printstatc: sta POINTERS + $05 + lda DFLTO + beq :+ + lda POINTERS + $05 +bsout: jmp $00 +: txa + pha + tya + pha + .if PLATFORM = diskio::platform::COMMODORE_128 + lda MMU_CR + pha + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | LOW_RAM | RAM_ROM + sta MMU_CR + .else; PLATFORM <> diskio::platform::COMMODORE_128 + lda IO_PORT + pha + lda #MEMCONFIG_CHARGEN + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + lda POINTERS + $05 + ldy #$00 + sty POINTERS + $05 + asl + rol POINTERS + $05 + asl + rol POINTERS + $05 + asl + sta POINTERS + $04 + lda POINTERS + $05 + rol + adc #.hibyte(CHARSET_ADDR_UPPERLOWER) + sta POINTERS + $05 + + ldx #$08 +: lda (POINTERS + $04),y + sta (POINTERS + $02),y + iny + inc POINTERS + $02 + inc POINTERS + $02 + dex + bne :- + + lda POINTERS + $02 + sbc #$0e + sta POINTERS + $02 + + and #%00111111 + cmp #$03 + beq :+ + cmp #$03 + $08 * 3 + bne :++ +: lda #$40 - 3 - 1 + adc POINTERS + $02 + sta POINTERS + $02 + bcc :+ + inc POINTERS + $03 +: +printstatr: + pla + .if PLATFORM = diskio::platform::COMMODORE_128 + sta MMU_CR + .else; PLATFORM <> diskio::platform::COMMODORE_128 + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + pla + tay + pla + tax + rts + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + +.if PLATFORM = diskio::platform::COMMODORE_16 + +ramirq: pha + txa + pha + tya + pha + sta TED_ROM_BANK_0 + tsx + inx + inx + inx + inx + lda STACK,x + and #BREAK + bne :+ + jmp (CINV) +: jmp (CBINV) + +clockirq: LOADER_IRQ_HANDLER_PROLOGUE + + jsr update_clk + jmp irqendx + + ; IRQ handler to switch on bitmap mode after the upper border area + +irq0: LOADER_IRQ_HANDLER_PROLOGUE + + lda #INVALID_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta TED_CTRL1; set scrolly + +consolswtc: lda #0 + beq :+ + + ; display console + lda #CHARSET_BITMAP_IN_ROM + ora TED_BITMAP_ADDR + tax + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR($0c00) + ldy #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + bne :++ + +: lda #.lobyte(~(CHARSET_BITMAP_IN_ROM)) + and TED_BITMAP_ADDR + tax + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR(COLRAM) + ldy #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 +: stx TED_BITMAP_ADDR + sta TED_SCREEN_ADDR + +: lda TED_RASTERLINE_MSB + lsr + bcs :- + lda #$01 +: cmp TED_RASTERLINE + bcs :- + nop + nop + STABILIZE_RASTER + + ldx #$07 +: dex + bne :- + + sty TED_CTRL1; disable invalid mode + + lda #.lobyte(SCREEN_SIZE) + sta TED_CURSOR_LO + lda #.hibyte(SCREEN_SIZE) + sta TED_CURSOR_HI + + lda #.lobyte(~IRQ_RASTERLINE_MSB) + and TED_IMR + sta TED_IMR + + ldx #1 + lda consolswtc + 1 + beq :+ + dex + stx BORDERCOLOUR +: stx setborder + 1 + txa + beq :+ + lda bordercol + 1 + cmp BORDERCOLOUR + beq :+ + sta BORDERCOLOUR +: + ; the last piture display line is 200 + 3 - 1 = 202 ($ca), + ; at the end of it, set the raster count back + ; to 203 - 17 = 186 = $ba (NTSC) or + ; to 203 - 33 = 170 = $aa (PAL) + sec + lda #$c7 + sbc IRQSLACK + ldx #.lobyte(irq1) + ldy #.hibyte(irq1) + jmp irqend + + ; IRQ handler to extend the screen + +irq1: LOADER_IRQ_HANDLER_PROLOGUE + + lda #$b0 + sta TED_CURSOR_LO + lda #$00 + sta TED_CURSOR_HI + + lda #$10; NTSC + ldx palntsc + beq :+ + lda #$18; PAL +: sta lineoffset + + lda #$c9 +: cmp TED_RASTERLINE + bcs :- + ; $ca + nop + nop + STABILIZE_RASTER + + inc TED_CTRL1 + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR(COLRAM + $0800) + sta TED_SCREEN_ADDR + sec + lda TED_RASTERLINE; $ca +lineoffset = * + 1 + sbc #$00; $10 (NTSC) or $18 (PAL) + sta TED_RASTERLINE + ldx #$08 +: dex + bne :- + lda #CHARSET_BITMAP_IN_ROM + ora TED_BITMAP_ADDR + sta TED_BITMAP_ADDR + lda #INVALID_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_4 + sta TED_CTRL1; hide garbage + lda TED_CHARGEN_ADDR + and #.lobyte(~CHARGEN_ADDR_MASK) + ora #MAKE_CHARGEN_ADDR(CHARSET_ADDR_UPPERLOWER) + sta TED_CHARGEN_ADDR + + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_4 + sta TED_CTRL1; switch to text mode + lda #$07 + ora TED_VERTSUBCOUNT + sta TED_VERTSUBCOUNT + lda #.lobyte(~MULTICOLOUR_MODE) + and TED_CTRL2 + sta TED_CTRL2 + + sec + lda #$da + sbc lineoffset; PAL: $da - $18 = $c2 + ldx IRQSLACK + bne :+ + sbc IRQSLACK + ldx #.lobyte(irq2) + ldy #.hibyte(irq2) + jmp irqend + +: cmp TED_RASTERLINE + bcs :- + jmp irq2main + + ; this handler is called just before the 2nd extra char row has finished displaying + +irq2: LOADER_IRQ_HANDLER_PROLOGUE + +irq2main: lda TED_CHARGEN_ADDR + and #.lobyte(~CHARGEN_ADDR_MASK) + ora #MAKE_CHARGEN_ADDR(COLRAM + $0c00) + tax + sec + lda #$db + sbc lineoffset + tay + + lda palntsc + bne :+++; branch if PAL + + ; NTSC +: cpy TED_RASTERLINE + bcs :- + nop + nop + STABILIZE_RASTER + + lda #INVALID_MODE | DISPLAY_ENABLE | LINES_24 | SCROLLY_4 + sta TED_CTRL1; open border + ldy #$06 +: dey + bne :- + nop + lda #.lobyte(~CHARSET_BITMAP_IN_ROM) + and TED_BITMAP_ADDR + jmp :+++ + +: lda #.lobyte(~CHARSET_BITMAP_IN_ROM) + and TED_BITMAP_ADDR +: cpy TED_RASTERLINE + bcs :- + +: stx TED_CHARGEN_ADDR + sta TED_BITMAP_ADDR + + lda palntsc + beq ntscborder + + lda TED_CHARGEN_ADDR + and #.lobyte(~CHARGEN_ADDR_MASK) + ora #MAKE_CHARGEN_ADDR(COLRAM + $0c00) + sta TED_CHARGEN_ADDR + + lda #$ca + ldx IRQSLACK + bne :+ + sec + sbc IRQSLACK + ldx #.lobyte(irq3) + ldy #.hibyte(irq3) + jmp irqend + +: cmp TED_RASTERLINE + bcs :- + jmp irq3main + + ; this handler is called just before the 3rd extra char row has finished displaying + +irq3: LOADER_IRQ_HANDLER_PROLOGUE + +irq3main: lda #FORCE_SINGLE_CLOCK + ora TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + + lda #$ca +: cmp TED_RASTERLINE + bcs :- + + lda #INVALID_MODE | DISPLAY_ENABLE | LINES_24 | SCROLLY_4 + sta TED_CTRL1; open border + + lda TED_RASTERLINE +: cmp TED_RASTERLINE + beq :- + nop + STABILIZE_RASTER + +ntscborder: lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR(COLRAM) + sta TED_SCREEN_ADDR + lda #.lobyte(~CHARSET_BITMAP_IN_ROM) + and TED_BITMAP_ADDR + sta TED_BITMAP_ADDR + lda palntsc + bne :++ + ldx #$08 +: dex + bne :- +: clc + lda lineoffset + adc TED_RASTERLINE + sta TED_RASTERLINE + + jsr update_clk + + lda TED_CHARGEN_ADDR + and #.lobyte(~(CHARGEN_ADDR_MASK | FORCE_SINGLE_CLOCK)) + ora #MAKE_CHARGEN_ADDR(COLRAM + $0c00) + tax + lda #ALL_COLUMNS + sta PIO_KEYBOARD_COLUMN + sta TED_KEYBOARD_LATCH + lda TED_KEYBOARD_LATCH + eor #NO_ROWS + sta consolswtc + 1 + beq :+ + lda TED_CHARGEN_ADDR + and #.lobyte(~CHARGEN_ADDR_MASK) + ora #MAKE_CHARGEN_ADDR(CHARSET_ADDR_UPPERLOWER) + tax +: stx TED_CHARGEN_ADDR + + lda IRQSLACK + beq :++ + lda #IRQ_RASTERLINE_MSB + ora TED_IMR + sta TED_IMR + sec + lda palntsc + bne :+ + lda #.lobyte(DISPLAY_LINES_NTSC) + SKIPWORD +: lda #.lobyte(DISPLAY_LINES_PAL) + sbc IRQSLACK +: ldx #.lobyte(irq0) + ldy #.hibyte(irq0) + +irqend: sta TED_IRQ_RASTERLINE + stx CINV + sty CINV + 1 +irqendx: lda #RASTER_IRQ + sta TED_IRR +irqendy: + +memcfgchks = * + $01 + ldx #0 + beq memchkok + + .if !LOAD_VIA_KERNAL_FALLBACK + lda TED_CHARGEN_ADDR + lsr + bcc memchkok + rol + tax + tay + lda #MSGINVMCONF + jmp error + .endif + +memchkok: LOADER_IRQ_HANDLER_EPILOGUE + pla + tay + pla + tax + pla + rti + +update_clk: + .if COUPLED_TIMERS = 0 + jsr readctrfix + tya + pha + txa + pha + sec + lda prevctr3lo + stx :+ + 1 +: sbc #$00 + tax + lda prevctr3hi + sty :+ + 1 +: sbc #$00 + tay + txa + clc + adc cycles_lo + sta cycles_lo + tya + adc cycles_mid + sta cycles_mid + bcc :+ + inc cycles_hi + +: pla + sta prevctr3lo + pla + sta prevctr3hi + .endif; COUPLED_TIMERS = 0 + + sed + ldx #$00 + clc + lda #$01 + adc tod_frames + sta tod_frames + ldy palntsc + beq :+ + cmp #5 + jmp :++ +: cmp #6 +: bne tod_done + stx tod_frames + clc + lda #$01 + adc tod_10s + sta tod_10s + cmp #$10 + bne tod_done + stx tod_10s + clc + lda #$01 + adc tod_secs + sta tod_secs + cmp #$60 + bne tod_done + stx tod_secs + clc + lda #$01 + adc tod_mins + sta tod_mins + cmp #$60 + bne tod_done + stx tod_mins + clc + lda #$01 + adc tod_hrs + sta tod_hrs +tod_done: cld + rts + + .if COUPLED_TIMERS = 0 +readctr: ldy TED_COUNTER3_HI + ldx TED_COUNTER3_LO + cpy TED_COUNTER3_HI + bne readctr + rts + +readctrfix: lda TED_CHARGEN_ADDR + and #FORCE_SINGLE_CLOCK + pha + bne :+ + lda #FORCE_SINGLE_CLOCK + php + sei + ora TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp +: + ldx TED_COUNTER3_LO + ldy TED_COUNTER3_HI + cpx TED_COUNTER3_LO + bcs :+ + ldx TED_COUNTER3_LO + ldy TED_COUNTER3_HI + nop + jmp :++ +: txa + sbc #$0e + tax + tya + sbc #$00 + tay +: + pla + bne :+ + lda #.lobyte(~FORCE_SINGLE_CLOCK) + php + sei + and TED_CHARGEN_ADDR + sta TED_CHARGEN_ADDR + plp +: rts + +readcycles: lda cycles_hi + sta cycshibuf + lda cycles_mid + sta cycsmidbuf + + lda prevctr3lo + sta prevctr3lb + lda prevctr3hi + sta prevctr3hb + + jsr readctr; counter runs backwards + stx ctr3lobuf + sty ctr3hibuf + + ldx cycles_lo +cycsmidbuf = * + $01 + lda #$00 + cmp cycles_mid + bne readcycles +cycshibuf = * + $01 + lda #$00 + cmp cycles_hi + bne readcycles + + stx cycslobuf + + sec +prevctr3lb = * + $01 + lda #$00 +ctr3lobuf = * + $01 + sbc #$00 + tax +prevctr3hb = * + $01 + lda #$00 +ctr3hibuf = * + $01 + sbc #$00 + tay + + clc + txa +cycslobuf = * + $01 + adc #$00 + pha + tya + adc cycsmidbuf + tax + lda #$00 + adc cycshibuf + tay + pla + rts + +docalibrte: jsr calibinits + nop; ldx #byteslo + nop; ldy #byteshi + nop; jsr printstat + nop + nop + nop; stx statxbuf + nop + nop; sty statybuf + nop + nop; jsr diffcycles + nop + nop + + ; fall through + +diffcycles: jsr readcycles + + sec + sbc prvcycslo + sta numcycles + $00 + txa + sbc prvcycsmid + sta numcycles + $01 + tya + sbc prvcycshi + sta numcycles + $02 + lda #$00 + sta numcycles + $03 + + sec + lda numcycles + $00 +adjustdiff = * + $01 + sbc #$00 + sta numcycles + $00 + bcs :+ + lda numcycles + $01 + dec numcycles + $01 + tax + bne :+ + lda numcycles + $02 + dec numcycles + $02 + tax + bne :+ + dec numcycles + $03 +: rts + .endif; COUPLED_TIMERS = 0 + +.else; PLATFORM <> diskio::platform::COMMODORE_16 + +ramirq: pha + txa + tsx + pha + tya + pha + .if PLATFORM = diskio::platform::COMMODORE_128 + lda MMU_CR + pha + lda #BANK_0 | SYSTEM_ROM | BASIC_HI | BASIC_LO | IO_SPACE + sta MMU_CR + .endif; PLATFORM = diskio::platform::COMMODORE_128 + inx + inx + lda STACK,x + and #FLAG_B + bne :+ + jmp (CINV) +: jmp (CBINV) + +consoleirq: + .if PLATFORM <> diskio::platform::COMMODORE_128 + lda IO_PORT + pha + lda #MEMCONFIG_IO + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + lda #0 + sta BORDERCOLOUR + + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $3b + sta VIC2_CTRL1; set 25 rows bit + ldx #VIC2_MAKE_ADDR($0400, CHARSET_ADDR_UPPERLOWER) + bit drivetype + bpl :++; branch if not generic drive + ; KERNAL fallback active + ; note: this may glitch due to a race condition, the KERNAL ROM's serial bus code does + ; things like lda $dd00 : and/ora #value : sta $dd00, which may be interrupted + ; between lda and sta by this IRQ, thus setting the previous VIC bank again + lda CIA2_PRA + and #.lobyte(~VIC2_BANK) + ora #VIC2_MAKE_BANK $0400 + tay + lda #$09 - 1 +: cmp VIC2_RASTERLINE + bcs :- + stx VIC2_ADDR + sty CIA2_PRA + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #IO_PORT_CHARGEN_ENABLED + sta IO_PORT + .endif; PLATFORM = diskio::platform::COMMODORE_128 + jmp :++ +: stx VIC2_ADDR + SET_VIC_BANK VIC2_MAKE_BANK $0400 + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #IO_PORT_CHARGEN_ENABLED + sta IO_PORT + .endif; PLATFORM = diskio::platform::COMMODORE_128 +: + .if !LOAD_TO_UPPER_MEM + jmp prepirq + + .else ; LOAD_TO_UPPER_MEM + + sec + lda #$f8 + sbc IRQSLACK + ldx #.lobyte(irq1) + ldy #.hibyte(irq1) + jmp irqend + + ; IRQ handler to switch back the bank setting, + ; needed because the sprites are in another + ; VIC bank than the bitmap + +irq0: + .if PLATFORM <> diskio::platform::COMMODORE_128 + lda IO_PORT + pha + lda #MEMCONFIG_IO + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + lda #VIC2_MAKE_ADDR COLRAM, BITMAP + sta VIC2_ADDR + SET_VIC_BANK VIC2_MAKE_BANK BITMAP + + sec + lda #$f8 + sbc IRQSLACK + ldx #.lobyte(irq1) + ldy #.hibyte(irq1) + jmp irqend + + .endif; LOAD_TO_UPPER_MEM + + ; IRQ handler to open the y-border +irq1: + .if PLATFORM <> diskio::platform::COMMODORE_128 + lda IO_PORT + pha + lda #MEMCONFIG_IO + sta IO_PORT + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + .if USE_2_MHZ + lda VIC2_C128_CLOCK + pha + lda #0 + sta VIC2_C128_CLOCK + .endif + lda #$f8 +: bit VIC2_CTRL1 + bmi :+ + cmp VIC2_RASTERLINE + bcs :- +: + lda #.lobyte(~LINES_25) + and VIC2_CTRL1 + sta VIC2_CTRL1 + + lda #$f9 +: cmp VIC2_RASTERLINE + beq :- + ldx #VIC2_MAKE_ADDR COLRAM, BITMAP + bit drivetype + bpl :++; branch if not generic drive + ; KERNAL fallback active + ; note: this may glitch due to a race condition, the KERNAL ROM's serial bus code does + ; things like lda $dd00 : and/ora #value : sta $dd00, which may be interrupted + ; between lda and sta by this IRQ, thus setting the previous VIC bank again + ldy #$07 +: dey + bne :- + lda CIA2_PRA + and #.lobyte(~VIC2_BANK) + ora #VIC2_MAKE_BANK BITMAP + sta CIA2_PRA + jmp :+++ +: lda #VIC2_MAKE_BANK BITMAP + ldy #$08 +: dey + bne :- + sta CIA2_PRA,y +: stx VIC2_ADDR + + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #IO_PORT_CHARGEN_DISABLED + sta IO_PORT + lda #%11111111 + sta VIC2_SPR_ENABLE + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + .if LOAD_TO_UPPER_MEM + lda #$fa +: bit VIC2_CTRL1 + bmi :+ + cmp VIC2_RASTERLINE + bcs :- +: + lda #VIC2_MAKE_ADDR SPRITESCR, BITMAP + sta VIC2_ADDR + bit drivetype + bpl :+; branch if not generic drive + ; KERNAL fallback active + ; note: this may glitch due to a race condition, the KERNAL ROM's serial bus code does + ; things like lda $dd00 : and/ora #value : sta $dd00, which may be interrupted + ; between lda and sta by this IRQ, thus setting the previous VIC bank again + lda CIA2_PRA + and #.lobyte(~VIC2_BANK) + ora #VIC2_MAKE_BANK SPRITES + sta CIA2_PRA + jmp :++ +: SET_VIC_BANK VIC2_MAKE_BANK SPRITES +: + .endif + + .if USE_2_MHZ + pla + sta VIC2_C128_CLOCK + .endif + + lda #$fb +: bit VIC2_CTRL1 + bmi :+ + cmp VIC2_RASTERLINE + bcs :- +: + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $3b + sta VIC2_CTRL1; set 25 rows bit + lda #SINGLECOLOUR_MODE | COLUMNS_40 | SCROLLX_0; $08 + sta VIC2_CTRL2 + + .if LITTLE_CPU_FOR_LOADER + inc BORDERCOLOUR + lda #$f5 +: cmp VIC2_RASTERLINE + bne :- + dec BORDERCOLOUR + .endif + + lda #ALL_COLUMNS + sta CIA1_PRA + lda CIA1_PRB + cmp #NO_ROWS + beq :++ + bit drivetype + bpl :+; branch if not generic drive + lda #$00 + SKIPWORD +: lda #$08 + ldx #.lobyte(consoleirq) + ldy #.hibyte(consoleirq) + bne irqend; jmp + +: lda #1 + SKIPWORD +prepirq: lda #0 + sta setborder + 1 + tax + beq :+ + lda BORDERCOLOUR + eor bordercol + 1 + and #%00001111 + beq :+ + lda bordercol + 1 + sta BORDERCOLOUR +: + .if LOAD_TO_UPPER_MEM + bit drivetype + bmi :+ + lda #$01 + ldx #.lobyte(irq0) + ldy #.hibyte(irq0) + bne irqend; jmp + +: bit VIC2_CTRL1 + bpl :- + lda #$1c +: cmp VIC2_RASTERLINE + bcs :- + lda #VIC2_MAKE_ADDR COLRAM, BITMAP + sta VIC2_ADDR + ; KERNAL fallback active + ; note: this may glitch due to a race condition, the KERNAL ROM's serial bus code does + ; things like lda $dd00 : and/ora #value : sta $dd00, which may be interrupted + ; between lda and sta by this IRQ, thus setting the previous VIC bank again + lda CIA2_PRA + and #.lobyte(~VIC2_BANK) + ora #VIC2_MAKE_BANK BITMAP + sta CIA2_PRA + .endif + sec + lda #$f8 + sbc IRQSLACK + ldx #.lobyte(irq1) + ldy #.hibyte(irq1) + +irqend: sta VIC2_RASTERLINE + stx CINV + $00 + sty CINV + $01 + + .if KEY_FOR_WATCHDOG + lda #ALL_COLUMNS + sta CIA1_PRA + ldx CIA1_PRB + inx + beq :++ + lda #.lobyte(~(CIA_SERIAL_DATA_IN_OUTPUT | CIA_SERIAL_CLK_IN_OUTPUT)) + sta CIA2_DDRA + lda #VIC2_BANK_MASK + and CIA2_PRA + sta CIA2_PRA +: inc BORDERCOLOUR + jmp :- +: + .endif + lda #RASTER_IRQ + sta VIC2_IRR + + .if PLATFORM = diskio::platform::COMMODORE_128 + pla + pha + +memcfgchks = * + $01 + ldx #$00 + beq memchkok + + .if LOAD_TO_UPPER_MEM + .if LOAD_VIA_KERNAL_FALLBACK + cmp #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE; $0e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM; $3f + beq memchkok + .else + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM; $3f + beq memchkok + .endif + .else + .if LOAD_VIA_KERNAL_FALLBACK + cmp #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE; $0e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM; $3f + beq memchkok + .else + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE; $3e + beq memchkok + cmp #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM; $3f + beq memchkok + .endif + .endif + + tax; faulty config value displayed in x and y + tay + lda #MSGINVMCONF + jmp error +memchkok: + + .else; PLATFORM <> diskio::platform::COMMODORE_128 + + pla + sta IO_PORT + + and #%00111111; mask out the upper 2 bits (C-128 CAPS LOCK (ASCII/DIN) and Flash8 clock control bits) + ora #CASSETTE_SENSE; this is 0 on SX-64 + +memcfgchks = * + 1 + ldx #$00 + beq memchkok + + .if LOAD_TO_UPPER_MEM + .if LOAD_VIA_KERNAL_FALLBACK + cmp #MEMCONFIG_ALL_RAM + beq memchkok + cmp #MEMCONFIG_IO + beq memchkok + cmp #MEMCONFIG_IO_KERNAL_BASIC + beq memchkok + .else + cmp #MEMCONFIG_ALL_RAM + beq memchkok + cmp #MEMCONFIG_IO + beq memchkok + .endif + .else + .if LOAD_VIA_KERNAL_FALLBACK + cmp #MEMCONFIG_IO + beq memchkok + cmp #MEMCONFIG_IO_KERNAL_BASIC + beq memchkok + .else + cmp #MEMCONFIG_IO + beq memchkok + .endif + .endif + + tax; faulty config value displayed in x and y + tay + lda #MSGINVMCONF + jmp error +memchkok: + .endif; PLATFORM <> diskio::platform::COMMODORE_128 + + .if PLATFORM = diskio::platform::COMMODORE_128 + jmp BUF; leaveirq + +leaveirq: pla + sta MMU_CR + .endif; PLATFORM = diskio::platform::COMMODORE_128 + pla + tay + pla + tax + pla + rti +leaveirqe: + +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + +nmihandler: lda #SPURIOUSNMI + jmp error + +brkhandler: pla + sta brkyreg + 1 + pla + sta brkxreg + 1 + pla + sta printa + pla + pla + .if PLATFORM = diskio::platform::COMMODORE_128 + pla + .endif; PLATFORM = diskio::platform::COMMODORE_128 + cld + sec + sbc #$02 + sta brkaddrlo + pla + sbc #$00 + sta brkaddrhi + + lda #ERRBRKOPC +brkxreg: ldx #0 +brkyreg: ldy #0 + jmp error + +.rodata + +startupmsg: .byte PETSCII_CLEAR, PETSCII_LO_UP_CASE + .byte PETSCII_WHITE, "Loader test application", PETSCII_RETURN + .byte PETSCII_CYAN, "by Krill/Plush", PETSCII_RETURN + .byte PETSCII_GREEN, "Repository version ", REPOSITORY_VERSION_STRING, PETSCII_RETURN + .byte PETSCII_LIGHTGREEN, "built on ", DATETIME, PETSCII_RETURN + .byte PETSCII_RETURN, 0 + +detectedms: .byte PETSCII_YELLOW, "Detected ", PETSCII_CYAN, 0 +systemmsg: .byte PETSCII_YELLOW, " system", PETSCII_RETURN, 0 +acfreqmsg: .byte PETSCII_YELLOW, " Hz AC frequency", PETSCII_RETURN, 0 +inssizemsg: .byte PETSCII_YELLOW, "Installer size is ", PETSCII_CYAN, "$" +inssizebyt: .byte 0, 0, 0, 0 + .byte PETSCII_YELLOW, " bytes", PETSCII_RETURN, 0 +ressizemsg: .byte PETSCII_YELLOW, "Resident size is ", PETSCII_CYAN, "$" +ressizebyt: .byte 0, 0, 0, 0 + .byte PETSCII_YELLOW, " bytes", PETSCII_RETURN, 0 +.if TEST_SAVE +trnsizemsg: .byte PETSCII_YELLOW, "Transient size is ", PETSCII_CYAN, "$" +trnsizebyt: .byte 0, 0, 0, 0 + .byte PETSCII_YELLOW, " bytes", PETSCII_RETURN, 0 +.endif +installmsg: .byte PETSCII_YELLOW, "Installing loader on dev ", PETSCII_CYAN, "#", 0 +installrst: .byte PETSCII_YELLOW, "... ", PETSCII_RETURN, 0 +runningmsg: .byte PETSCII_YELLOW, "Running on ", 0 +quotemsg: .byte PETSCII_LIGHTGREEN, '"', 0 +returnmsg: .byte PETSCII_RETURN, 0 +twobitatnm: .byte PETSCII_YELLOW, "Using ", PETSCII_CYAN, "2bit+ATN", PETSCII_YELLOW, " transfers", PETSCII_RETURN, 0 +burstmsg: .byte PETSCII_YELLOW, "Using ", PETSCII_CYAN, "burst+ATN" +.if ASYNCHRONOUS_BURST_HANDSHAKE + .byte "+CLK" +.endif + .byte PETSCII_YELLOW, " transfers", PETSCII_RETURN, 0 +loopmsg: +.byte PETSCII_YELLOW, "Running test loop", PETSCII_RETURN +.if PLATFORM <> diskio::platform::COMMODORE_128 + .byte PETSCII_RETURN, PETSCII_RETURN, PETSCII_RETURN, PETSCII_RETURN + .byte PETSCII_RETURN, PETSCII_RETURN + .if TEST_SAVE + .else + .byte PETSCII_RETURN + .endif +.endif + .byte 0 +looprunmsg: .byte PETSCII_YELLOW, "Test loop run #", PETSCII_CYAN, 0 +mhzmsg: .byte ", ", PETSCII_CYAN +mhz: .byte "1", PETSCII_YELLOW, " MHz", 0 +logmsg: .byte PETSCII_CYAN, 0 +kbsspcmsg: .byte PETSCII_CYAN +spacemsg: .byte " ", 0 + +paltext: .byte "PAL", 0 +ntsctext: .byte "NTSC", 0 + +drivemsgsl: .byte .lobyte(msg1541), .lobyte(msg1541c), .lobyte(msg1541ii), .lobyte(msg1541u) + .byte .lobyte(msg1570), .lobyte(msg1571), .lobyte(msg1571cr), .lobyte(msg1581), .lobyte(msgfd2000), .lobyte(msgfd4000) + .byte .lobyte(msgenerics - 1) +drivemsgsr: .byte .lobyte(msgenerics) +drivemsgbr: .byte .lobyte(msgenericb) +drivemsgpa: .byte .lobyte(msgenericp) + +drivemsgsh: .byte .hibyte(msg1541), .hibyte(msg1541c), .hibyte(msg1541ii), .hibyte(msg1541u) + .byte .hibyte(msg1570), .hibyte(msg1571), .hibyte(msg1571cr), .hibyte(msg1581), .hibyte(msgfd2000), .hibyte(msgfd4000) + .byte .hibyte(msgenerics - 1) + .byte .hibyte(msgenerics) + .byte .hibyte(msgenericb) + .byte .hibyte(msgenericp) + +msg1541: .byte PETSCII_CYAN, "CBM 1541", 0 +msg1541c: .byte PETSCII_CYAN, "CBM 1541-C", 0 +msg1541ii: .byte PETSCII_CYAN, "CBM 1541-II", 0 +msg1541u: .byte PETSCII_CYAN, "1541U", 0 +msg1570: .byte PETSCII_CYAN, "CBM 1570", 0 +msg1571: .byte PETSCII_CYAN, "CBM 1571", 0 +msg1571cr: .byte PETSCII_CYAN, "CBM 1571CR", 0 +msg1581: .byte PETSCII_CYAN, "CBM 1581", 0 +msgfd2000: .byte PETSCII_CYAN, "CBM FD 2000", 0 +msgfd4000: .byte PETSCII_CYAN, "CBM FD 4000", 0 +msgenerics: .byte PETSCII_CYAN, "generic serial drive", 0 +msgenericb: .byte PETSCII_CYAN, "generic burst drive", 0 +msgenericp: .byte PETSCII_CYAN, "generic parallel drive", 0 + +drivtypesl: .byte .lobyte(str1541), .lobyte(str1541c), .lobyte(str1541ii), .lobyte(str1541u) + .byte .lobyte(str1570), .lobyte(str1571), .lobyte(str1571cr), .lobyte(str1581), .lobyte(strfd2000), .lobyte(strfd4000) + .byte .lobyte(strgenrics - 1) + .byte .lobyte(strgenrics) + .byte .lobyte(strgenricb) + .byte .lobyte(strgenricp) + +drivtypesh: .byte .hibyte(str1541), .hibyte(str1541c), .hibyte(str1541ii), .hibyte(str1541u) + .byte .hibyte(str1570), .hibyte(str1571), .hibyte(str1571), .hibyte(str1581), .hibyte(strfd2000), .hibyte(strfd4000) + .byte .hibyte(strgenrics - 1) + .byte .hibyte(strgenrics) + .byte .hibyte(strgenricb) + .byte .hibyte(strgenricp) + + ; "123456789" max length +str1541: .byte "CBM1541" , 0 +str1541c: .byte "CBM1541C" , 0 +str1541ii: .byte "CBM1541II", 0 +str1541u: .byte "1541U" , 0 +str1570: .byte "CBM1570" , 0 +str1571: .byte "CBM1571" , 0 +str1571cr: .byte "CBM1571CR", 0 +str1581: .byte "CBM1581" , 0 +strfd2000: .byte "CMDFD2000", 0 +strfd4000: .byte "CMDFD4000", 0 +strgenrics: .byte "gn serial", 0 +strgenricb: .byte "gen burst", 0 +strgenricp: .byte "parallel", 0 + +kbscoltext: .byte PETSCII_YELLOW +kbstext: .byte " kB/s", 0 + + .byte " " +emptytexte: .byte 0 + +.if LOAD_TO_UPPER_MEM + +pic1unc: .byte "ab-pic1hiram.bin", 0 + .if LOAD_VIA_KERNAL_FALLBACK | VERIFY +pic2unc: .byte "bb-pic2hiram.bin", 0 + .else +pic2unc: .byte 0; use loadnext functionality + .endif + + .if DECOMPRESSOR <> DECOMPRESSORS::NONE +pic1compd: .byte "ap-pic1hiram.pak", 0 + .if LOAD_VIA_KERNAL_FALLBACK | VERIFY +pic2compd: .byte "bp-pic2hiram.pak", 0 + .else +pic2compd: .byte 0; use loadnext functionality + .endif + .else +pic1compd: +pic2compd: .byte 0 + .endif + +.else; !LOAD_TO_UPPER_MEM + +pic1unc: .byte "r1-raw", 0 + .if LOAD_VIA_KERNAL_FALLBACK | VERIFY +pic2unc: .byte "r2-raw", 0 + .else +pic2unc: .byte 0; use loadnext functionality + .endif + + .if DECOMPRESSOR <> DECOMPRESSORS::NONE +pic1compd: .byte "p1-pak", 0 + .if LOAD_VIA_KERNAL_FALLBACK | VERIFY +pic2compd: .byte "p2-pak", 0 + .else +pic2compd: .byte 0; use loadnext functionality + .endif + .else +pic1compd: +pic2compd: .byte 0 + .endif + +.endif + +bogusname: .byte $ff, $ff, $ff, $ff + .byte $ff, $ff, $ff, $ff + .byte $ff, $ff, $ff, $ff + .byte $ff, $ff, $ff, $ff, 0 + +errormsgsl: .repeat $78, I + .byte .lobyte(invaliderr) + .endrep + +ERRPALNTSC = * - errormsgsl + .byte .lobyte(emgpalntsc) +ERRLOADADDR = * - errormsgsl + .byte .lobyte(emsglodadd) +ERRENDADDR = * - errormsgsl + .byte .lobyte(emsgendadd) +ERRBYTESLOADED = * - errormsgsl + .byte .lobyte(emsgbytlod) +ERRSAFETYMARGIN = * - errormsgsl + .byte .lobyte(emsgsafety) +MSGUNINST = * - errormsgsl + .byte .lobyte(emsguninst) +ERRBRKOPC = * - errormsgsl + .byte .lobyte(emsgbrkopc) +SPURIOUSNMI = * - errormsgsl + .byte .lobyte(emsgspunmi) +MEMCONFCHNG = * - errormsgsl + .byte .lobyte(emsgmemcfg) +MSGINVMCONF = * - errormsgsl + .byte .lobyte(emsginvmcf) +MSGVERIFAIL = * - errormsgsl + .byte .lobyte(emsgverifl) + + .repeat $74, I + .byte .lobyte(invaliderr) + .endrep + + .byte .lobyte(emsgftoobg) + .byte .lobyte(emsgftoosm) + .byte .lobyte(emsgftoolr) + .byte .lobyte(emsgwritep) + .byte .lobyte(emsgdevinc) + .byte .lobyte(emsgtoomd) + .byte .lobyte(emsggenker) + .byte .lobyte(emsgdevnp) + .byte .lobyte(emsgfnotf); $ff + +errormsgsh: .repeat $78, I + .byte .hibyte(invaliderr) + .endrep + + .byte .hibyte(emgpalntsc) + .byte .hibyte(emsglodadd) + .byte .hibyte(emsgendadd) + .byte .hibyte(emsgbytlod) + .byte .hibyte(emsgsafety) + .byte .hibyte(emsguninst) + .byte .hibyte(emsgbrkopc) + .byte .hibyte(emsgspunmi) + .byte .hibyte(emsgmemcfg) + .byte .hibyte(emsginvmcf) + .byte .hibyte(emsgverifl) + + .repeat $74, I + .byte .hibyte(invaliderr) + .endrep + + .byte .hibyte(emsgftoobg) + .byte .hibyte(emsgftoosm) + .byte .hibyte(emsgftoolr) + .byte .hibyte(emsgwritep) + .byte .hibyte(emsgdevinc) + .byte .hibyte(emsgtoomd) + .byte .hibyte(emsggenker) + .byte .hibyte(emsgdevnp) + .byte .hibyte(emsgfnotf); $ff + +.assert errormsgsh - errormsgsl = $0100, error, "errormsgsl has wrong length" +.assert * - errormsgsh = $0100, error, "errormsgsh has wrong length" + +emgpalntsc: .byte "PAL/NTSC detection mismatch. ", 0 +emsglodadd: .byte "Wrong load address. ", 0 +emsgendadd: .byte "Wrong end address. ", 0 +emsgbytlod: .byte "Wrong amount of bytes loaded. ", 0 +emsgsafety: .byte "Safety margin exceeded.", 0 +emsginvmcf: .byte "Invalid memory configuration. ", 0 +cmsguninst: .byte PETSCII_WHITE +emsguninst: .byte "The loader is now uninstalled. ", 0 +emsgbrkopc: .byte "Executed BRK instruction at $", 0 +emsgspunmi: .byte "Spurious NMI. ", 0 +emsgmemcfg: .byte "Memory config changed. ", 0 +emsgverifl: .byte "Verify failed at $" +verifysrc: .byte "0000/$" +verifydest: .byte "0000. ", 0 + +emsgftoobg: .byte "File too large. ", 0 +emsgftoosm: .byte "File on disk too small. ", 0 +emsgftoolr: .byte "File on disk too large. ", 0 +emsgwritep: .byte "Write protect on. ", 0 +emsgdevinc: .byte "Device incompatible. ", 0 +emsgtoomd: .byte "Too many devices. ", 0 +emsggenker: .byte "Generic KERNAL error. ", 0 +emsgdevnp: .byte "Device not present. ", 0 +emsgfnotf: .byte "File not found. ", 0 + +invaliderr: .byte "Invalid error code. ", 0 + +numccpal: CYCLES_PER_SECOND_PAL +numccntsc: CYCLES_PER_SECOND_NTSC + +.bss + +palntsc: .res 1 +drivetype: .res 1 + +.if PLATFORM = diskio::platform::COMMODORE_16 +tod_hrs: .res 1 +tod_mins: .res 1 +tod_secs: .res 1 +tod_10s: .res 1 +tod_frames: .res 1 + + .if COUPLED_TIMERS = 0 +prevctr3lo: .res 1 +prevctr3hi: .res 1 + +cycles_hi: .res 1 +cycles_mid: .res 1 +cycles_lo: .res 1 + +prvcycslo: .res 1 +prvcycsmid: .res 1 +prvcycshi: .res 1 + .endif; COUPLED_TIMERS +.endif + +numcycles: .res 4 +cyclesperd: .res 5 +hundred: .res 5 + +capsname: + +.segment "END" diff --git a/loader/samples/turndisk/Linkfile b/loader/samples/turndisk/Linkfile new file mode 100644 index 0000000..fc16914 --- /dev/null +++ b/loader/samples/turndisk/Linkfile @@ -0,0 +1,14 @@ +MEMORY +{ + LOADERZP: start = $02, size = $fe, type = rw; + RAM: start = $1c00, size = $c000, type = rw; +} + +SEGMENTS +{ + CODE: load = RAM, type = ro; + + DISKIO_ZP: load = LOADERZP, type = zp, define = yes; + DISKIO: load = RAM, align = 256, define = yes; + DISKIO_INSTALL: load = RAM, define = yes; +} diff --git a/loader/samples/turndisk/Makefile b/loader/samples/turndisk/Makefile new file mode 100755 index 0000000..b2d8d16 --- /dev/null +++ b/loader/samples/turndisk/Makefile @@ -0,0 +1,225 @@ + +ifeq ($(PLATFORM),) +_PLATFORM_ = c64 +else ifeq ($(PLATFORM),c116) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),plus4) +_PLATFORM_ = c16 +else +_PLATFORM_ = $(PLATFORM) +endif + +ifeq ($(NO_VICE),) +NO_VICE = 0 +endif + + +ifneq ($(_PLATFORM_),c64) +ifneq ($(_PLATFORM_),c128) +ifneq ($(_PLATFORM_),c16) +$(error invalid platform $(_PLATFORM_) specified) +endif +endif +endif + + +ARCH = $(shell uname | tr "[a-z]" "[A-Z]" | tr -c -d "[A-Z]") + +ifneq ($(findstring CYGWINNT,$(ARCH)),) + ifeq (CYGWINNT,$(ARCH)) +ARCH = WIN32 + else +ARCH = WIN64 + endif +endif +ifneq ($(findstring DARWIN,$(ARCH)),) +ARCH = MACOSX +endif + + +ifeq ($(_PLATFORM_),c16) + ifneq ($(NO_VICE),0) + ifneq ($(findstring WIN,$(ARCH)),) +USE_PLUS4EMU = 0 +USE_YAPE = 1 + else +USE_PLUS4EMU = 1 +USE_YAPE = 0 + endif + else +USE_PLUS4EMU = 0 +USE_YAPE = 0 + endif +else +USE_PLUS4EMU = 0 +USE_YAPE = 0 +endif + + +ifeq ($(_PLATFORM_),c16) + ifeq ($(ARCH),MACOSX) + # MacOSX, these programs must be installed as applications +VICE = xplus4 +PLUS4EMU = open /Applications/plus4emu.app --args + else +VICE = xplus4 +PLUS4EMU = plus4emu + ifeq ($(ARCH),WIN64) +YAPE = YapeWin64 + else +YAPE = Yape + endif + endif +else + ifeq ($(_PLATFORM_),c128) +VICE = x128 + else +VICE = x64 + endif +endif + +ifneq ($(USE_PLUS4EMU),0) +EMU = $(PLUS4EMU) -disk +EMU70 = $(PLUS4EMU) -disk +else +EMU = $(VICE) -drive8type 1541 -drive9type 0 -autostart +EMU70 = $(VICE) -drive8type 1570 -drive9type 0 -autostart +endif + + +ECHO = echo +PRINTF = printf + +AS = ca65 +LD = ld65 +PU = ../../tools/pucrunch/pucrunch +C1541 = c1541 + +MKDIR = mkdir -p +RM = rm -f +ifeq ($(ARCH),MACOSX) +RMDIR = rmdir # XXX TODO xargs to remove .DS_Store +else +RMDIR = rmdir +endif +CAT = cat + + +.PHONY: default loader assemble link compress diskimages run clean distclean wipe +.PHONY: tellarch + + +BUILDDIR = ../../build +INTERMDIR = ../../build/intermediate +LOADER_SRC = ../../src +LOADER = $(BUILDDIR)/loader-$(_PLATFORM_).lib + +RESOURCESDIR = ../resources +PIC1 = $(INTERMDIR)/pic1.bin +PIC2 = $(INTERMDIR)/pic2.bin + +NAME = turndisk + +SOURCE = $(NAME).s +LOADERCFG = loaderconfig.inc +ASSEMBLE = $(INTERMDIR)/$(NAME)-$(_PLATFORM_).o +LINK = $(INTERMDIR)/$(NAME)-uncompressed-$(_PLATFORM_).prg +COMPRESS = $(INTERMDIR)/$(NAME)-$(_PLATFORM_).prg +DISKIMAGE_A = $(BUILDDIR)/$(NAME)-$(_PLATFORM_)-a.d64 +DISKIMAGE_B = $(BUILDDIR)/$(NAME)-$(_PLATFORM_)-b.d64 +DISKIMAGES = $(DISKIMAGE_A) $(DISKIMAGE_B) + +AS_FLAGS = -I ../../../shared -I ../../include -I $(LOADER) -D EXTCONFIGPATH +PU_FLAGS = -d -l 0x1c00 -x 0x1c00 -i 0 + + +default: diskimages + + +tellarch: + @$(ECHO) $(ARCH) + +.PHONY: $(LOADER) +loader: $(LOADER) + +$(LOADER): $(LOADERCFG) + make -C $(LOADER_SRC) EXTCONFIGPATH=../samples/$(NAME) lib + + +assemble: $(ASSEMBLE) + +$(ASSEMBLE): $(SOURCE) $(LOADERCFG) + $(MKDIR) $(BUILDDIR) + $(MKDIR) $(INTERMDIR) +ifeq ($(_PLATFORM_),c64) + $(AS) $(AS_FLAGS) -t c64 -D PLATFORM=64 -o $@ $< +else ifeq ($(_PLATFORM_),c128) + $(AS) $(AS_FLAGS) -t c128 -D PLATFORM=128 -o $@ $< +else + $(AS) $(AS_FLAGS) -t c16 -D PLATFORM=16 -o $@ $< +endif + + +link: $(LINK) + +$(LINK): Linkfile $(ASSEMBLE) $(LOADER) + $(LD) -o $@ -C $^ + + +compress: $(COMPRESS) + +$(COMPRESS): $(LINK) +ifeq ($(_PLATFORM_),c64) + $(PU) $(PU_FLAGS) -c64 -g 0x37 $^ $@ +else ifeq ($(_PLATFORM_),c128) + $(PU) $(PU_FLAGS) -c128 $^ $@ +else + $(PU) $(PU_FLAGS) -c16 $^ $@ +endif + + +diskimages: $(DISKIMAGES) + +$(DISKIMAGE_A): $(COMPRESS) $(PIC1) + $(C1541) -format "normal is boring,+h" d64 $@ + $(C1541) -attach $@ \ + -write $(COMPRESS) "$(NAME)" \ + -write $(PIC1) "pic1" + +$(DISKIMAGE_B): $(PIC2) + $(C1541) -format "normal is boring,+h" d64 $@ + $(C1541) -attach $@ \ + -write $(COMPRESS) "$(NAME)" \ + -write $(PIC2) "pic2" + + +ifneq ($(USE_YAPE),0) +run: diskimages + $(YAPE) "..\..\build\$(DISKIMAGE_A)" + +run70: diskimages + $(YAPE) "..\..\build\$(DISKIMAGE_A)" +else +run: diskimages + $(EMU) $(realpath $(DISKIMAGE_A)) + +run70: diskimages + $(EMU70) $(realpath $(DISKIMAGE_A)) +endif + + +$(INTERMDIR)/%.bin: $(RESOURCESDIR)/%.bin + $(PRINTF) '\000\140' | $(CAT) - $? > $@ # octal 140 = hex 60 + + +clean: + -$(RM) $(INTERMDIR)/$(NAME)-c64.o $(INTERMDIR)/$(NAME)-uncompressed-c64.prg $(INTERMDIR)/$(NAME)-c64.prg $(BUILDDIR)/$(NAME)-c64-a.d64 $(BUILDDIR)/$(NAME)-c64-b.d64 + -$(RM) $(INTERMDIR)/$(NAME)-c128.o $(INTERMDIR)/$(NAME)-uncompressed-c128.prg $(INTERMDIR)/$(NAME)-c128.prg $(BUILDDIR)/$(NAME)-c128-a.d64 $(BUILDDIR)/$(NAME)-c128-b.d64 + -$(RM) $(INTERMDIR)/$(NAME)-c64.o $(INTERMDIR)/$(NAME)-uncompressed-c16.prg $(INTERMDIR)/$(NAME)-c16.prg $(BUILDDIR)/$(NAME)-c16-a.d64 $(BUILDDIR)/$(NAME)-c16-b.d64 + -$(RMDIR) $(INTERMDIR) + -$(RMDIR) $(BUILDDIR) + +distclean: + -$(MAKE) -C $(LOADER_SRC) clean + +wipe: distclean clean diff --git a/loader/samples/turndisk/loaderconfig.inc b/loader/samples/turndisk/loaderconfig.inc new file mode 100755 index 0000000..bf057ec --- /dev/null +++ b/loader/samples/turndisk/loaderconfig.inc @@ -0,0 +1,131 @@ + +; configuration +; set .defines to non-0 to enable the corresponding features + +; see loader.inc for function calls and convenience macros + +; parameters + +.ifndef PLATFORM +PLATFORM = diskio::platform::COMMODORE_64; available are COMMODORE_64, COMMODORE_128 and COMMODORE_16 +.endif + +; parameter, this changes the host-side code only + +DECOMPRESSOR = DECOMPRESSORS::NONE; available are NONE, BITNAX (recommended for demos), BYTEBOOZER2, DOYNAX_LZ, EXOMIZER (not recommended for demos), LEVELCRUSH, LZSA2 (recommended for demos), NUCRUNCH, PUCRUNCH, SUBSIZER, TINYCRUNCH (recommended for demos), TSCRUNCH (strongly recommended for demos), ZX0 (strongly recommended for demos) + + +; features + +; following settings are independent from the installed drive code, several host-side +; resident binaries with different features may be used with the same installed drive code + + +; basic features, different settings can be run with the same installed drive code, increase host-side code size + +.define LOAD_COMPD_API 0 ; include the loadcompd routine to load and depack compressed files on the fly + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + +.define LOAD_RAW_API 1 ; include the loadraw routine to load files without decompressing + +.define NTSC_COMPATIBILITY 0 ; C-64/128 only: be able to run on both PAL and NTSC machines, this slightly decreases loading speed on PAL, + ; note that PAL vs. NTSC is not detected by the install routine, and no error is returned when running on an + ; NTSC machine with the NTSC_COMPATIBILITY option disabled: detect, then select either of both incarnations + ; of the resident portion (with and without NTSC support) for maximum speed with NTSC and PAL + +.define PREFER_SPEED_OVER_SIZE 0 ; For TSCrunch or ZX0, use a bigger but potentially faster decompression routine + +.define UNINSTALL_API 0 ; include an uninstallation routine + + +; extended features, different settings can be run with the same installed drive code, increase host-side code size + +.define FILE_EXISTS_API 1 ; include the fileexists routine for simple multi-disk handling + +.define LOAD_UNDER_D000_DFFF 0 ; C-64/128: enable loading (and decompression) to the RAM at $D000..$DFFF, + ; note that this does not slow down loading when not loading to RAM at $D000..$DFFF, + ; as there are two separate routines to load data (one regular, the other to RAM at $D000..$DFFF). + ; the IRQ handlers will need to change $01 ($FF00 on C-128) to enable the I/O registers at $D000..$DFFF, + ; so make sure the IRQ handlers restore the $01 status on C-64 to the value as when they are called. + ; the IRQs must run via $FFFE/F, since the ROM is disabled when accessing the RAM at $D000-$DFFF + ; this is not needed when only memdecompressing to $D000..$DFFF (simply set $01 to $30 on C-64 and jsr memdecomp in that case) + +.define ALLOW_2_MHZ_ON_C128 0 ; C-64 only: allow 2 MHz usage on C-128 in C-64 mode, + ; this does not increase raw loading speed but increases combined loading + decompression speed using loadcompd. + ; the clock is temporarily switched to 1 MHz while loading, + ; interrupt handlers changing the clock speed must restore it upon return to the mainline thread. + +.define MEM_DECOMP_API 0 ; include a routine for memory decompression, that is, loading and decompression can be separated. + ; C-64: decompression to $D000..$DFFF need not have LOAD_UNDER_D000_DFFF enabled, + ; just enable 64 kB of RAM before jsr memdecomp. + ; requires DECOMPRESSOR != DECOMPRESSORS::NONE + ; this option does not implicitly turn on the LOAD_RAW_API + +.define MEM_DECOMP_TO_API 0 ; if carry is set on decompression, the decompressor will use the address set in decdestlo/decdesthi as + ; decompression destination address and ignore the file's decompression address. + ; requires MEM_DECOMP_API != 0 + +.define LOAD_TO_API 0 ; if the carry flag is set on load, override load and decompression destination addresses. + ; load raw files: use the address set in loadaddrlo/loadaddrhi as absolute loading address + ; load compressed files: use relative loading address offset in loadaddroffslo/loadaddroffshi, it is added to the load/depack addresses + +.define END_ADDRESS_API 0 ; during and after loading, the file's current and then final end address (address of last file byte + 1) is stored in + ; endaddrlo and endaddrhi. for loading compressed files using loadcompd, the end address of the compressed data is stored. + ; the file's loading address can be found in loadaddrlo/loadaddrhi during and after loading, so polling the current + ; difference of endaddrlo/hi and loadaddrlo/hi can be used to implement progress displays. + +.define LOAD_VIA_KERNAL_FALLBACK 0 ; loads via the KERNAL API if drive code installation was not successful + ; (i.e., if it cannot installed due to an incompatible drive - possible if it is not + ; a 1541, 1541-C, 1541-II, 1541U, 1570, 1571, 1571CR, 1581, or FD2000/4000), + ; or true drive emulation being disabled. + ; note that this does not necessarily mean slow or non-IRQ loading, as custom KERNALs like JiffyDOS or IDEDOS + ; use the original KERNAL API as well. + ; the IRQ handlers can be delayed for some rasterlines up to several frames due to KERNAL routines + ; temporarily disabling IRQ (but that is unlikely for devices not using the serial bus). + ; for the sake of compatibility, only disable this option if the space is really needed. + ; C-64: + ; Attention: KERNAL, BASIC, and possible cartridge ROMs are enabled, so IRQ handlers are not + ; allowed in the ranges $8000..$BFFF and $D000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE), both are possible - + ; best have KERNAL and BASIC enabled before calling the loader, so only the KERNAL vector IRQ handler is + ; needed (please note that the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-128: + ; Attention: System ROM is enabled, so IRQ handlers are not allowed in the range $C000..$FFFF. + ; Attention: KERNAL routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via KERNAL vector ($0314) vs. non-KERNAL vector ($FFFE) - best have System ROM + ; enabled before calling the loader, so only the KERNAL vector IRQ handler is needed (please note that + ; the handler code is delayed a little when called via $0314 rather than $FFFE). + ; C-64/128: + ; Attention: KERNAL routines use CIA1 timer A ($DC04/5). + ; Plus/4: + ; Attention: The ROM space in the upper memory half is enabled, so IRQ handlers are not allowed + ; in the range $8000..$FFFF. + ; Attention: The ROM routines may execute CLI, so make sure to have valid IRQ vectors and handlers, + ; or disable all IRQ sources (not via SEI), also make sure to correctly handle the different + ; IRQ conditions when called via ROM vector ($0314) vs. non-ROM vector ($FFFE) - best have ROM enabled + ; before calling the loader, so only the ROM vector IRQ handler is needed (please note that + ; the handler code is delayed a little when being called via $0314 rather than $FFFE). + ; requires ONLY_1541_AND_COMPATIBLE = 0 + +.define CLOSE_FILE_API 0 ; include the closefile call to close an open file + + +; these options change drive-side code + +.define DIRTRACK 18 ; actual directory track, this can be changed to have a shadow directory so that the + ; normal directory does not list the files and can be used entirely for bootstrap and dir-art +.define DIRTRACK81 40 ; (i.e., the loader's directory can be relocated to hide it from the normal directory command). + ; DIRTRACK must be 18 when LOAD_VIA_KERNAL_FALLBACK != 0 + ; DIRTRACK81 must be 40 when LOAD_VIA_KERNAL_FALLBACK != 0 + +.define FILENAME_MAXLENGTH 16 ; maximum length of filename, if a directory is capable of holding longer names, extra characters are ignored, + ; to facilitate dir-art, set to, e.g., 2, then load files as "01*", "02*", etc. + + +; this reduces host-side install code + +.define ONLY_1541_AND_COMPATIBLE 0 ; reduces host-side install code by omitting any native custom drive code for non-1541 compatible + ; drives, treats any drive as 1541, using an incompatible drive will cause undefined behaviour diff --git a/loader/samples/turndisk/turndisk.s b/loader/samples/turndisk/turndisk.s new file mode 100755 index 0000000..acdbe4f --- /dev/null +++ b/loader/samples/turndisk/turndisk.s @@ -0,0 +1,100 @@ + +.include "standard.inc" + +.include "loader.inc" + +.if PLATFORM = diskio::platform::COMMODORE_16 + .include "ted.inc" +.else + .include "vic.inc" +.endif + + +screen = $5800 +bitmap = $6000 + +one_bits = COLOUR_DARKGREY +zero_bits = COLOUR_MEDIUMGREY + + + MEMSET #bitmap, #BITMAP_SIZE, #BITMAP_BACKGROUND + +.if PLATFORM = diskio::platform::COMMODORE_16 + lda #$00 ; disable all interrupts, + sta TED_IMR; as KERNAL routines do cli + lda TED_IRR; with LOAD_VIA_KERNAL_FALLBACK + sta TED_IRR + + MEMSET #screen, #SCREEN_SIZE, #MAKE_HIRES_INTENSITIES(one_bits, zero_bits) + MEMSET #screen + PAD(SCREEN_SIZE), #SCREEN_SIZE, #MAKE_HIRES_COLOURS(one_bits, zero_bits) +.else + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #IO_PORT_CHARGEN_DISABLED + sta IO_PORT + lda #NO_INTERRUPTS + sta VIC2_IMR + .endif + lda #CIA_CLR_INTF | EVERY_IRQ; disable KERNAL timer interrupts + sta CIA1_ICR + bit CIA1_ICR + + MEMSET #screen, #SCREEN_SIZE, #MAKE_HIRES_COLOURS(one_bits, zero_bits) +.endif + DISPLAY_HIRES_BITMAP bitmap, screen + + lda #COLOUR_BLACK + sta BORDERCOLOUR + + LOADER_INSTALL + bcs error + +.if FILE_EXISTS_API + +side1file: FILEEXISTS filename1; filename1 exists only on the first side + bcc :+; branch on success + cmp #diskio::status::FILE_NOT_FOUND + bne error + inc BORDERCOLOUR + jmp side1file +: + LOADRAW filename1; filename1 is only found on the first side + bcs error + +side2file: FILEEXISTS filename2; filename2 exists only on the second side + bcc :+; branch on success + cmp #diskio::status::FILE_NOT_FOUND + bne error + dec BORDERCOLOUR + jmp side2file +: + LOADRAW filename2; filename2 is only found on the second side + bcc side1file + bcs error; jmp + +.else; !FILE_EXISTS_API + +side1file: LOADRAW filename1; filename1 is only found on the first side + bcc side2file; branch on success + cmp #diskio::status::FILE_NOT_FOUND + bne error + + inc BORDERCOLOUR + jmp side1file + +side2file: LOADRAW filename2; filename2 is only found on the second side + bcc side1file; branch on success + cmp #diskio::status::FILE_NOT_FOUND + bne error + + dec BORDERCOLOUR + jmp side2file + +.endif; FILE_EXISTS_API + +error: ldx #COLOUR_BLACK +: sta BORDERCOLOUR + stx BORDERCOLOUR + jmp :- + +filename1: .asciiz "pic1" +filename2: .asciiz "pic2" diff --git a/loader/src/Makefile b/loader/src/Makefile new file mode 100755 index 0000000..7bb5af8 --- /dev/null +++ b/loader/src/Makefile @@ -0,0 +1,271 @@ + +INSTALL ?= 1000 +RESIDENT ?= 0200 +TRANSIENT ?= 4000 +ZP ?= e0 + +export _PLATFORM_ INSTALL RESIDENT TRANSIENT ZP + +ifeq ($(PLATFORM),) +_PLATFORM_ = c64 +else ifeq ($(PLATFORM),c128) +_PLATFORM_ = c128 +else ifeq ($(PLATFORM),c16) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),c116) +_PLATFORM_ = c16 +else ifeq ($(PLATFORM),plus4) +_PLATFORM_ = c16 +else +_PLATFORM_ = $(PLATFORM) +endif + +ifneq ($(_PLATFORM_),c64) +ifneq ($(_PLATFORM_),c128) +ifneq ($(_PLATFORM_),c16) +$(error invalid platform $(_PLATFORM_) specified) +endif +endif +endif + + +TOP ?= . + +SVNVERSION = svnversion +ifneq ($(wildcard ../../.svn/format),) +VERSION = $(shell $(SVNVERSION) | tr -d [:cntrl:]) +else +VERSION = $(shell $(GREP) -oP 'VERSION_STRING "\K[^"]+' ../version.inc) +endif + +CD = cd +ECHO = echo +PRINTF = printf +CAT = cat +CP = cp +MV = mv +RM = rm -f +MKDIR = mkdir +RMDIR = rmdir +GREP = ggrep + +AS = ca65 +LD = ld65 +AR = ar65 +VICE = x64 +C1541 = c1541 + +PERL = perl +ZIP = zip -9 + +INCDIR = $(TOP)/../../shared +LDRINC = $(TOP)/../include + +DECOMPDIR = $(TOP)/decompress +DRIVESDIR = $(TOP)/drives +HALDIR = $(TOP)/hal + +BUILDDIR = $(TOP)/../build +INTERMDIR = $(TOP)/../build/intermediate + +ifneq ($(EXTCONFIGPATH),) +CONFIG = $(EXTCONFIGPATH)/loaderconfig.inc +else +CONFIG = $(LDRINC)/config.inc +endif + +INSTALLDEPS = $(CONFIG) $(BUILDDIR) $(INTERMDIR) \ + $(DRIVESDIR)/drivecode-common.inc $(DRIVESDIR)/drivecode1541.s $(DRIVESDIR)/drivecode1571.s $(DRIVESDIR)/drivecode1581.s \ + $(HALDIR)/hal.inc $(HALDIR)/hal-c64-c128.inc $(HALDIR)/hal-c16.inc \ + install.s \ + $(LDRINC)/loader.inc \ + $(TOP)/../version.inc Makefile + +RESIDENTDEPS = $(CONFIG) $(BUILDDIR) $(INTERMDIR) \ + $(DECOMPDIR)/bitnaxdecomp.s \ + $(DECOMPDIR)/b2decomp.s \ + $(DECOMPDIR)/doynaxdecomp.s \ + $(DECOMPDIR)/exodecomp.s \ + $(DECOMPDIR)/lcdecomp.s \ + $(DECOMPDIR)/lzsa2decomp.s \ + $(DECOMPDIR)/ncdecomp.s \ + $(DECOMPDIR)/pudecomp.s \ + $(DECOMPDIR)/subsizerdecomp.s \ + $(DECOMPDIR)/tcdecomp.s \ + $(DECOMPDIR)/zx0decomp.s \ + $(HALDIR)/hal.inc $(HALDIR)/hal-c64-c128.inc $(HALDIR)/hal-c16.inc \ + resident.s \ + customdrivecode.s \ + save.s \ + $(LDRINC)/loader.inc \ + $(TOP)/../version.inc Makefile + +BINDEPS = $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc $(BUILDDIR)/loader-$(_PLATFORM_).prg $(CONFIG) + +# allow undocumented opcodes +ifeq ($(_PLATFORM_),c64) +AS_FLAGS = --cpu 6502X +else ifeq ($(_PLATFORM_),c128) +AS_FLAGS = --cpu 6502X +else ifeq ($(_PLATFORM_),c16) +AS_FLAGS = --cpu 6502X +else +# do not allow undocumented opcodes +AS_FLAGS = --cpu 6502 +endif + +AS_FLAGS += -g # include symbols in object files +ifeq ($(_PLATFORM_),c16) +AS_FLAGS += -t c16 -D PLATFORM=16 +else ifeq ($(_PLATFORM_),c128) +AS_FLAGS += -t c128 -D PLATFORM=128 +else +AS_FLAGS += -t c64 -D PLATFORM=64 +endif +AS_FLAGS += -I $(TOP)/. -I $(INCDIR) -I $(LDRINC) +ifneq ($(EXTCONFIGPATH),) +AS_FLAGS += -D EXTCONFIGPATH -I $(EXTCONFIGPATH) +endif + +.PHONY: lib prg bin binary prgzip binzip binaryzip customdrivecode save + +default: prg + +all: lib prg + +# use these targets to build loader-.prg and install-.prg along with loadersymbols-.inc +prg bin binary: $(BINDEPS) + @$(ECHO) "Usage: $(MAKE) prg INSTALL= RESIDENT= ZP= [PROJECT=]" + @$(ECHO) "INSTALL=\$$$(INSTALL)" + @$(ECHO) "RESIDENT=\$$$(RESIDENT)" + @$(ECHO) "ZP=\$$$(ZP)" + @$(ECHO) "PROJECT=$(PROJECT)" + $(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +prgzip binzip binaryzip: $(BUILDDIR)/loader-$(_PLATFORM_).zip + +customdrivecode: $(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg + @$(ECHO) "Usage: $(MAKE) customdrivecode INSTALL= RESIDENT= TRANSIENT= ZP= [PROJECT=]" + @$(ECHO) "INSTALL=\$$$(INSTALL)" + @$(ECHO) "RESIDENT=\$$$(RESIDENT)" + @$(ECHO) "TRANSIENT=\$$$(TRANSIENT)" + @$(ECHO) "ZP=\$$$(ZP)" + @$(ECHO) "PROJECT=$(PROJECT)" + $(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +save: $(BUILDDIR)/save-$(_PLATFORM_).prg + @$(ECHO) "Usage: $(MAKE) save INSTALL= RESIDENT= TRANSIENT= ZP= [PROJECT=]" + @$(ECHO) "INSTALL=\$$$(INSTALL)" + @$(ECHO) "RESIDENT=\$$$(RESIDENT)" + @$(ECHO) "TRANSIENT=\$$$(TRANSIENT)" + @$(ECHO) "ZP=\$$$(ZP)" + @$(ECHO) "PROJECT=$(PROJECT)" + $(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +lib: $(BUILDDIR)/loader-$(_PLATFORM_).lib + + +$(TOP)/../version.inc: + $(MAKE) -C $(TOP)/.. version.inc + + +# directory targets + +$(BUILDDIR): + $(MKDIR) $@ + +$(INTERMDIR): + $(MKDIR) $@ + + +# object targets + +$(INTERMDIR)/install-$(_PLATFORM_).o: $(INSTALLDEPS) + $(AS) $(AS_FLAGS) -I $(INTERMDIR) -o $@ install.s # allow undocumented opcodes for drive code + +$(INTERMDIR)/customdrivecode-$(_PLATFORM_).o: $(RESIDENTDEPS) + $(AS) $(AS_FLAGS) -o $@ customdrivecode.s + +$(INTERMDIR)/save-$(_PLATFORM_).o: $(RESIDENTDEPS) + $(AS) $(AS_FLAGS) -o $@ save.s + +$(INTERMDIR)/resident-$(_PLATFORM_).o: $(RESIDENTDEPS) + $(AS) $(AS_FLAGS) -o $@ resident.s + + +# binary targets + +.PHONY: $(INTERMDIR)/binary.link +.PHONY: $(BUILDDIR)/loader-$(_PLATFORM_).prg $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o +.PHONY: $(BUILDDIR)/install-$(_PLATFORM_).prg $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o +.PHONY: $(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg $(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o +.PHONY: $(BUILDDIR)/save-$(_PLATFORM_).prg $(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o +.PHONY: $(INTERMDIR)/loader.map $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +$(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o: $(RESIDENTDEPS) + $(AS) $(AS_FLAGS) -D RESIADDR=0x$(RESIDENT) -o $@ resident.s + +$(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o: $(RESIDENTDEPS) + $(AS) $(AS_FLAGS) -D INSTADDR=0x$(INSTALL) -o $@ install.s + +$(INTERMDIR)/binary.link: Makefile + $(PERL) make-linkfile.pl $(_PLATFORM_) $(ZP) $(INSTALL) $(RESIDENT) $(TRANSIENT) > $@ + $(CAT) $@ + +$(BUILDDIR)/loader-$(_PLATFORM_).prg: $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/binary.link + $(LD) -C $(INTERMDIR)/binary.link -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o + $(RM) a.out + +$(BUILDDIR)/install-$(_PLATFORM_).prg: $(BUILDDIR)/loader-$(_PLATFORM_).prg + +$(INTERMDIR)/loader-$(_PLATFORM_).map: $(BUILDDIR)/install-$(_PLATFORM_).prg + + +$(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o: customdrivecode.s + $(AS) $(AS_FLAGS) -D TRNSADDR=0x$(TRANSIENT) -o $@ $^ + +$(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg: $(INTERMDIR)/binary.link $(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o + $(LD) -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map -C $^ + $(MV) $(BUILDDIR)/transient-$(_PLATFORM_).prg $@ + $(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $(INTERMDIR)/loader-$(_PLATFORM_).map > $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +$(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o: save.s + $(AS) $(AS_FLAGS) -D TRNSADDR=0x$(TRANSIENT) -o $@ $^ + +$(BUILDDIR)/save-$(_PLATFORM_).prg: $(INTERMDIR)/binary.link $(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/customdrivecode-$(_PLATFORM_).o $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o + $(LD) -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map -C $^ + $(MV) $(BUILDDIR)/transient-$(_PLATFORM_).prg $@ + $(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $(INTERMDIR)/loader-$(_PLATFORM_).map > $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc + +ifeq ($(PROJECT),) +_PROJECT_ = loader +else +_PROJECT_ = $(PROJECT) +endif + +$(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc: $(INTERMDIR)/loader-$(_PLATFORM_).map + $(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $^ > $@ + +$(BUILDDIR)/loader-$(_PLATFORM_).zip: $(BINDEPS) + $(CD) $(BUILDDIR) \ + && $(CP) $(BUILDDIR)/loader-$(_PLATFORM_).prg $(INTERMDIR)/loader.prg && $(ZIP) -j $@ $(INTERMDIR)/loader.prg \ + && $(CP) $(BUILDDIR)/install-$(_PLATFORM_).prg $(INTERMDIR)/install.prg && $(ZIP) -j $@ $(INTERMDIR)/install.prg \ + && $(CP) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc $(INTERMDIR)/loadersymbols.inc && $(ZIP) -j $@ $(INTERMDIR)/loadersymbols.inc + + +# loader-$(_PLATFORM_).lib library target + +$(BUILDDIR)/loader-$(_PLATFORM_).lib: $(INTERMDIR)/resident-$(_PLATFORM_).o $(INTERMDIR)/install-$(_PLATFORM_).o $(INTERMDIR)/customdrivecode-$(_PLATFORM_).o $(INTERMDIR)/save-$(_PLATFORM_).o + $(AR) a $@ $^ + + +clean: + -$(RM) \ + $(INTERMDIR)/* \ + $(BUILDDIR)/loader-c64.lib \ + $(BUILDDIR)/loader-c64.prg $(BUILDDIR)/install-c64.prg $(BUILDDIR)/loadersymbols-c64.inc \ + $(BUILDDIR)/loader-c128.lib \ + $(BUILDDIR)/loader-c128.prg $(BUILDDIR)/install-c128.prg $(BUILDDIR)/loadersymbols-c128.inc \ + $(BUILDDIR)/loader-c16.lib \ + $(BUILDDIR)/loader-c16.prg $(BUILDDIR)/install-c16.prg $(BUILDDIR)/loadersymbols-c16.inc + -$(RMDIR) $(INTERMDIR) diff --git a/loader/src/customdrivecode.s b/loader/src/customdrivecode.s new file mode 100644 index 0000000..9305a84 --- /dev/null +++ b/loader/src/customdrivecode.s @@ -0,0 +1,660 @@ + +__NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT = 1 +.include "loader.inc" + +.include "drives/drivecode-common.inc" +.include "hal/hal.inc" + +.include "cpu.inc" +.scope drive +.include "via.inc" +.endscope +.include "cia.inc" + +.segment "DISKIO_PLUGIN_ZP" : zeropage + +CUSTOMPARAM: .res 2 +DRVCODEOFS: .res 1 +DRVCODEPTR: .res 2 +BUFFERPTR: .res 2 +.if ONLY_1541_AND_COMPATIBLE = 0 +DRIVETYPE: .res 1 +.endif; ONLY_1541_AND_COMPATIBLE + +UPLOADENTR: .res 2 +UPLOADTO: .res 2 +UPLOADCNT: .res 2 +UPLOADPTR: .res 2 + +plugin_zp_last = * - 1 +.export plugin_zp_last + +.segment "DISKIO_PLUGIN" + +.ifdef TRNSADDR + .org TRNSADDR - 2 + .word * + 2; load address +.endif + +.export swapdrvcod + +swapdrvcod: stx CUSTOMPARAM + sty CUSTOMPARAM + 1 + + stx DRVCODEPTR + sty DRVCODEPTR + 1 + +.if ONLY_1541_AND_COMPATIBLE = 0 + + lda #$80 + sta DRIVETYPE + BRANCH_IF_INSTALLED :+ + lda #diskio::status::DEVICE_INCOMPATIBLE + sec + rts +: +.endif; !ONLY_1541_AND_COMPATIBLE + + ldy #swapparams::buffer + lda (CUSTOMPARAM),y + sta BUFFERPTR + sta bufmain41 +.if ONLY_1541_AND_COMPATIBLE = 0 + sta bufmain71 + sta bufmain81 +.endif; !ONLY_1541_AND_COMPATIBLE + iny + lda (CUSTOMPARAM),y + sta BUFFERPTR + 1 + sta bufmain41 + 1 +.if ONLY_1541_AND_COMPATIBLE = 0 + sta bufmain71 + 1 + sta bufmain81 + 1 +.endif; !ONLY_1541_AND_COMPATIBLE + + CLEAR + +: SET_FLAGS_N_DATA_V_CLK + bvc :- + + ldy #$ff +: iny + lda trampoline,y + jsr sendbyte + bne :- + + ldy #swapparams::drivecode41 + jsr prepmove + ldx #drivecode::from - 1 +: lda UPLOADENTR,x + sta trampoln41 + payldprm41 - trmplrun41,x + dex + bpl :- + +.if ONLY_1541_AND_COMPATIBLE = 0 + lda #diskio::drivetype::DRIVES_1541 + + SET_FLAGS_N_DATA_V_CLK + bvc is1581 + bmi setdrvtype + + IDLE + + ldy #swapparams::drivecode71 + jsr prepmove + ldx #drivecode::from - 1 +: lda UPLOADENTR,x + sta trampoln71 + payldprm71 - trmplrun71,x + dex + bpl :- + + lda #diskio::drivetype::DRIVES_157X + bne setdrvtype; jmp + +is1581: IDLE + + ldy #swapparams::drivecode81 + jsr prepmove + ldx #drivecode::from - 1 +: lda UPLOADENTR,x + sta custom81 + payldprm81 - trampoln81,x + dex + bpl :- + + lda #diskio::drivetype::DRIVES_1581_CMD + +setdrvtype: sta DRIVETYPE +.endif; !ONLY_1541_AND_COMPATIBLE + + jsr selectdcod + + pha + ldy #drivecode::entry + jsr prepmove + pla + +.if ONLY_1541_AND_COMPATIBLE = 0 + + cmp #swapparams::drivecode81 + bne doupload + + ldy #drivecode::from +: lda UPLOADENTR - 1,y + jsr sendbyte + dey + bne :- + +.endif; !ONLY_1541_AND_COMPATIBLE + +doupload: jsr uploadcode + + ldy #ldrmain41 - drvcode41 - drivecode::length + jsr prepmove + + ; buffer loader drive code + CLEAR + +: SET_FLAGS_N_DATA_V_CLK + bvc :- + + ldy #0 +getdrvcode: jsr receivbyte + sta (BUFFERPTR),y + iny + bne :+ + inc BUFFERPTR + 1 +: inc UPLOADCNT + bne getdrvcode + inc UPLOADCNT + 1 + bmi getdrvcode + +: SET_FLAGS_N_DATA_V_CLK + bpl :- + + ; upload custom drive code + lda CUSTOMPARAM + sta DRVCODEPTR + lda CUSTOMPARAM + 1 + sta DRVCODEPTR + 1 + ldy DRVCODEOFS + jsr uplodpayld + + CLEAR + + OK_CLC + rts + +.export restoreldr + +restoreldr: +.if ONLY_1541_AND_COMPATIBLE = 0 + + lda DRIVETYPE + bpl :+ + lda #diskio::status::DEVICE_INCOMPATIBLE + sec + rts +: +.endif; !ONLY_1541_AND_COMPATIBLE + + INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + CLEAR + +waitrestor: SET_FLAGS_N_DATA_V_CLK + bpl waitrestor + bvc waitrestor + +.if ONLY_1541_AND_COMPATIBLE = 0 + + lda DRIVETYPE + +.endif; !ONLY_1541_AND_COMPATIBLE + + jsr selectdcod + + ldy #ldrmain41 - drvcode41 - drivecode::length + jsr uplodpayld + + INSTALL_IDLE + rts + +.export sendbyte + +sendbyte: pha + nop; delay + SENDBYTE + pla + rts + +.export receivbyte + +receivbyte: RECEIVEBYTE + rts + +selectdcod: +.if ONLY_1541_AND_COMPATIBLE = 0 + + cmp #diskio::drivetype::DRIVES_157X + bne check1581 + lda #swapparams::drivecode71 + ldx #.lobyte(drvcode71) + ldy #.hibyte(drvcode71) + bne dcodselctd; jmp + +check1581: cmp #diskio::drivetype::DRIVES_1581_CMD + bne upload1541 + lda #swapparams::drivecode81 + ldx #.lobyte(drvcode81) + ldy #.hibyte(drvcode81) + bne dcodselctd; jmp +upload1541: + +.endif; ONLY_1541_AND_COMPATIBLE + + lda #swapparams::drivecode41 + ldx #.lobyte(drvcode41) + ldy #.hibyte(drvcode41) + +dcodselctd: sta DRVCODEOFS + stx DRVCODEPTR + sty DRVCODEPTR + 1 + rts + +uplodpayld: jsr prepmove + +uploadcode: ldy #0 +: lda (UPLOADPTR),y + jsr sendbyte + iny + bne :+ + inc UPLOADPTR + 1 +: inc UPLOADCNT + bne :-- + inc UPLOADCNT + 1 + bmi :-- + rts + +prepmove: ldx #0 +: lda (DRVCODEPTR),y + iny + sta UPLOADENTR,x + inx + cpx #8 + bne :- + + ;sec + lda #0 + sbc UPLOADCNT + sta UPLOADCNT + lda #0 + sbc UPLOADCNT + 1 + sta UPLOADCNT + 1 + rts + +trampoline: + .scope upload41 + +.include "via.inc" + + .org $ef; FILENAME41 + +recvtrmpln: DRIVEGETBYTE 1541, trampolinecmp, nozeroes + sta $0105,x; BLOCKBUFFER41 + 5 + inx + cpx #custm41end - trampoln41 + 1 + bne recvtrmpln + .byte $ff + .word (entry - 1) +entry: lda getbytecmp41 + 1 + sta .lobyte(trampolinecmp + 1) + ldx #1 + ldy VIA1_PRB + 16 + bcs recvtrmpln; jmp + + .reloc + + ;.byte 0; end marker is 0 at drvcode41 + + .endscope + +drvcode41: .word 0; entry, not used here + .word trmplrun41; to + .word custm41end - trampoln41; length + .word trampoln41; from + +ldrmain41: .word $0700 + RETURNSIZE41; buffer length +bufmain41: .word 0; buffer + +trampoln41: + .org $0106; = BLOCKBUFFER41 + 6 + +RESTORELOOP41 = $d9; with SENDVAL41 +RESTOREMAIN41 = RESTORELOOP41 + 1 + +trmplrun41: .scope custom41 + +.include "drives/drivecode-common.inc" + +.include "via.inc" + + lda #ATNA_OUT | CLK_OUT | DATA_OUT + sta VIA1_PRB + + ; buffer end of read loop to make space for the receive routine + ldx #21 - 1 +: lda RESTORELOOP41,x + sta SECTORLINKTABLE41,x + dex + bpl :- + + lda getbytecmp41 + 1 + sta customloop + (getbytecmp41 + 1) - getbyte41 + + ; put receive routine for buffered loader code to zeropage + ldx #$0100 - (custmstore - customloop - 3) +: lda custmstore - $0100,x + sta .lobyte(restorloop - restrloope),x + inx + bne :- + ldx #$0100 - (restrloope - restorloop) +: lda restrloope - $0100,x + sta $00,x + inx + bne :- + + lda #ATNA_OUT + sta VIA1_PRB + lda #CLK_IN | DATA_IN +: bit VIA1_PRB + bne :- + +SENDVAL41 = RESTORELOOP41 + +sendzp: lda $00,x + DRIVESENDBYTE 1541, SENDVAL41 + inx + bne sendzp + + ldx #.lobyte(returnrun) +sendmain: lda returnrun & $ff00,x + DRIVESENDBYTE 1541, SENDVAL41 + inx + bne sendmain + inc sendmain + 2 + dec numpages41 + bne sendmain + + lda #ATNA_OUT | CLK_OUT | DATA_OUT + sta (V1B41,x); VIA1_PRB + + lda #OPC_RTS + sta getbyterts41 + + ldx #RESTOREMAIN41 +: lda .lobyte($0100 - RESTOREMAIN41),x + ldy payldpre41 - $0100,x + sty .lobyte($0100 - RESTOREMAIN41),x + sta $00,x; buffer low end of zeropage at high end of zeropage + inx + bne :- + + lda #ATNA_OUT + sta ($03,x); VIA1_PRB + + ldy VIA1_PRB + .byte OPC_JMP_ABS, 0; jmp $0000 + +numpages41: .byte 7 + +CUSTOMPARAM41 = $fa + +restorloop: sta (.lobyte(CUSTOMPARAM41 + 2),x); x = 0 + inc .lobyte(CUSTOMPARAM41 + 2) + bne :+ + inc .lobyte(CUSTOMPARAM41 + 3) +: inc .lobyte(CUSTOMPARAM41 + 4) + bne restorloop + 3 - (custmstore - customloop) + inc .lobyte(CUSTOMPARAM41 + 5) + bne restorloop + 3 - (custmstore - customloop) + .byte OPC_JMP_ABS + .word $0200 - RETURNSIZE41; entry + .word $0200 - RETURNSIZE41; to + .word $10000 - ($0600 + RETURNSIZE41); length +restrloope: + +UPLOADOFFS = custmstore - (customloop + 3) + + ; executed at $0000 +customloop: DRIVEGETBYTE 1541, getbyte +custmstore: sta (.lobyte(UPLOADOFFS + 19),x); x = 0 + inc .lobyte(UPLOADOFFS + 19) + bne :+ + inc .lobyte(UPLOADOFFS + 20) +: inc .lobyte(UPLOADOFFS + 21) + bne customloop + 3 + inc .lobyte(UPLOADOFFS + 22) + bne customloop + 3 + .byte OPC_JMP_ABS + + .endscope + +payldprm41: .word 0; entry + .word 0; to + .word 0; -length +payldpre41: + +return: + .org $01e0 + +returnrun: ldx #= CUSTOMUPLOADSIZE71, error, "***** 1571 custom upload code too small. *****" + + .reloc +custom71end: + +drvcode81: .word trampoln81; entry + .word trampoln81; to + .word custom81end - custom81; length + .word custom81; from + + .word DRVCODEND81 - $0300; buffer size +bufmain81: .word 0; buffer + +custom81: + .org $0b00; = BLOCKBUFFER81 + +trampoln81: lda #payldpre81 - payldprm81 - 1; = $05 = CLK_IN | DATA_IN + tax +: ldy payldprm81,x + sty CUSTOMPARAM81,x + dex + bpl :- + + ;lda #CLK_IN | DATA_IN +: bit CIA_PRB + bne :- + +SENDVAL81 = CUSTOMPARAM81 + 6 + + ; send loader code at $0300 + ldx #0 +sendcode81: lda a:$0300 & $ff00,x + DRIVESENDBYTE 1581, SENDVAL81 + inx + bne :+ + inc sendcode81 + 2 +: cpx #.lobyte(DRVCODEND81) + bne sendcode81 + lda sendcode81 + 2 + cmp #.hibyte(DRVCODEND81) + bne sendcode81 + + ldx #0 + stx CIA_PRB + + jmp CUSTOMRECEIVE81 + +payldprm81: .word 0; entry + .word 0; to + .word 0; -length +payldpre81: + .reloc +custom81end: + +.endif; !ONLY_1541_AND_COMPATIBLE diff --git a/loader/src/decompress/b2decomp.s b/loader/src/decompress/b2decomp.s new file mode 100755 index 0000000..6e3e7c9 --- /dev/null +++ b/loader/src/decompress/b2decomp.s @@ -0,0 +1,193 @@ +; ByteBoozer Decruncher /HCL May.2003 +; B2 Decruncher December 2014 +; with slight modifications by Krill + +decompress = Decrunch +decompsrc = Get1+1 + +.FEATURE labels_without_colons, leading_dot_in_identifiers + +;Variables.. #Bytes +zp_base = DECOMPVARS ; - +bits = zp_base ;1 + +put = decdestlo + + +.macro .GetNextBit +.local DgEnd + asl bits + bne DgEnd + jsr GetNewBits +DgEnd +.endmacro + +.macro .GetLen +.local GlEnd +.local GlLoop + lda #1 +GlLoop + .GetNextBit + bcc GlEnd + .GetNextBit + rol + bpl GlLoop +GlEnd +.endmacro + +Decrunch + jsr Gnb + lda loadaddrhi + sta Get2+2 + sta Get3+2 + ldx loadaddrlo + jsr GetNewBits + tya +.if LOADCOMPD_TO + clc + adc loadaddroffslo + php +.endif +storedadrl: + sta put + jsr GetNewBits + tya +.if LOADCOMPD_TO + plp + adc loadaddroffshi +.endif +storedadrh: + sta put + 1 + lda #$80 + sta bits + +DLoop + POLLBLOCK + + .GetNextBit + bcs Match +Literal + ; Literal run.. get length. + .GetLen + sta LLen+1 + + ldy #0 +LLoop +Get3 lda $ff00,x + inx + bne *+5 + jsr GnbInc + sta (put),y + iny +LLen cpy #0 + bne LLoop + + clc + tya + adc put + sta put + bcc *+4 + inc put+1 + + iny + beq DLoop + + ; Has to continue with a match.. + +Match + ; Match.. get length. + .GetLen + sta MLen+1 + + ; Length 255 -> EOF + cmp #$ff + beq End + + ; Get num bits + cmp #2 + lda #0 + rol + .GetNextBit + rol + .GetNextBit + rol + tay + lda Tab,y + beq MByte + + ; Get bits < 8 +MLoop1 .GetNextBit + rol + bcs MLoop1 + bmi MShort +MByte + ; Get byte + eor #$ff + tay +Get2 lda $ff00,x + inx + bne MLong + jsr GnbInc + jmp MLong +MShort + ldy #$ff +MLong + ;clc + adc put + sta MLda+1 + tya + adc put+1 + sta MLda+2 + + ldy #$ff +MLoop2 iny +MLda lda $b00b,y + sta (put),y +MLen cpy #0 + bne MLoop2 + + ;sec + tya + adc put + sta put + bcc *+4 + inc put+1 + + jmp DLoop + +GetNewBits +Get1 ldy $ff00,x + sty bits + rol bits + inx + beq GnbInc +End rts + +GnbInc + inc Get1+2 + inc Get2+2 + inc Get3+2 +Gnb + php + pha + tya + pha + GETBLOCK Get1+2 + pla + tay + pla + ldx #0 + plp + rts + +Tab + ; Short offsets + .byte %11011111 ; 3 + .byte %11111011 ; 6 + .byte %00000000 ; 8 + .byte %10000000 ; 10 + ; Long offsets + .byte %11101111 ; 4 + .byte %11111101 ; 7 + .byte %10000000 ; 10 + .byte %11110000 ; 13 diff --git a/loader/src/decompress/bitnaxdecomp.s b/loader/src/decompress/bitnaxdecomp.s new file mode 100755 index 0000000..6913373 --- /dev/null +++ b/loader/src/decompress/bitnaxdecomp.s @@ -0,0 +1,266 @@ + +decompsrc = bitfire_lz_sector_ptr1 + +.if MEM_DECOMP_TO_API + ; cannot copy remaining uncompressed blob of unknown size + .error "***** MEM_DECOMP_TO_API is not supported for BITNAX. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****" +.endif + +BITFIRE_DECOMP_ZERO_OVERLAP = 1 + +.FEATURE labels_without_colons, leading_dot_in_identifiers + +.lz_bits = DECOMPVARS + 0 +.lz_dst = DECOMPVARS + 1 +.lz_end = DECOMPVARS + 3 +.lz_tmp = DECOMPVARS + 5 + +decompress: + jsr .lz_next_page_ + lda loadaddrhi + sta bitfire_lz_sector_ptr2 + 1 + ldx loadaddrlo +.if BITFIRE_DECOMP_ZERO_OVERLAP + ldy #$03; destination and end addresses +.else + ldy #$01; destination address +.endif +: lda (loadaddrlo),y + sta .lz_dst,y + inx + bne :+ + jsr .lz_next_page +: dey + bpl :-- + +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc .lz_dst + sta .lz_dst + lda loadaddroffshi + adc .lz_dst+1 + sta .lz_dst+1 + .if BITFIRE_DECOMP_ZERO_OVERLAP + clc + lda loadaddroffslo + adc .lz_end + sta .lz_end + lda loadaddroffshi + adc .lz_end+1 + sta .lz_end+1 + .endif +.endif + sec + +.lz_type_refill + jsr .lz_refill_bits ;refill bit buffer .lz_bits + + ;******** Start the next match/literal run ******** +.lz_type_check + bcc .lz_do_match + beq .lz_type_refill ;we will fall through on entry + + ;******** Process literal run ******** + + lda #$00 +: + rol ;-> a = $01 after first round + asl .lz_bits + bne *+5 + jsr .lz_refill_bits ;kills y + bcc .lz_lrun_gotten + + asl .lz_bits + bne :- + jsr .lz_refill_bits + bne :- + +.lz_lrun_gotten + sta .lz_lcopy_len ;Store LSB of run-length + ldy #$00 +.lz_lcopy +bitfire_lz_sector_ptr2 = * + 1 ;Copy the literal data, forward or overlap is getting a pain in the ass. + lda $ff00,x + sta (.lz_dst),y + inx + bne :+ + clc + jsr .lz_next_page +: + iny +.lz_lcopy_len = * + 1 + cpy #$00 + bne .lz_lcopy + + tya +;.if LOAD_VIA_KERNAL_FALLBACK | (LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16)) + bne *+5 + jmp .lz_maximum ;maximum literal run, bump sector pointers and so on and force new type bit +;.else +; beq .lz_maximum ;maximum literal run, bump sector pointers and so on and force new type bit +;.endif + ;XXX TODO can we reuse the same code? In one case continue with match, in other case redecide + clc + adc .lz_dst + sta .lz_dst + bcc *+4 + inc .lz_dst+1 + + POLLBLOCK + ;no need for a type bit, after each literal a match follows, except for maximum runlength literals + + ;******** Process match ******** + +.lz_do_match + lda #$01 ;this could be made shorter by using the last bitfetch of the upcoming loop and restoring the carry again by a cmp #$02. Saves bytes, but makes things slower, as eof check is also done with all short matches then + + asl .lz_bits ;first length bit (where a one identifies + bne *+5 ;a two-byte match) + jsr .lz_refill_bits + bcc .lz_get_offs ;all done, length is 2, skip further bitfetches (and eof check) +: + asl .lz_bits + bne *+5 + jsr .lz_refill_bits + rol + + asl .lz_bits + bne *+5 + jsr .lz_refill_bits + bcc :- +.lz_got_len + tay ;XXX TODO could this be placed elsewhere to make the tay obsolete? + beq .lz_end_of_file ;A 257-byte (=>$00) run serves as a sentinel, but not with zero-overlap, except when depacking from a non inplace address, then it is still appended +.lz_get_offs + sta .lz_mcopy_len ;store length at final destination + + lda #%11000000 ;fetch 2 more prefix bits + rol ;previous bit is still in carry \o/ +: + asl .lz_bits + bne *+5 + jsr .lz_refill_bits + rol + bcs :- + + beq .lz_8_and_more ;0 + 8 bits to fetch, branch out before table lookup to save a few cycles and one byte in the table, also save complexity on the bitfetcher + tay + lda .lz_lentab,y +: ;same as above + asl .lz_bits ;XXX same code as above, so annoying :-( + bne *+5 + jsr .lz_refill_bits + rol + bcs :- + + bmi .lz_less_than_8 ;either 3,4,6 or 7 bits fetched -> highbyte will be $ff +.lz_8_and_more + jsr .lz_refill_bits + eor #$ff ;5 of 13, 2 of 10, 0 of 8 bits fetched as highbyte, lowbyte still to be fetched + sta .lz_tmp ;XXX this is a pain in the arse that A and Y need to be swapped :-( + tya + ldy .lz_tmp + SKIPWORD +.lz_less_than_8 + ldy #$ff ;XXX TODO silly, y is set twice in short case + adc .lz_dst ;subtract offset from lz_dst + sta .lz_m+1 + tya ;hibyte + adc .lz_dst+1 + sta .lz_m+2 + + ldy #$ff ;The copy loop. This needs to be run + ;forwards since RLE-style matches can overlap the destination +.lz_mcopy + iny +.lz_m lda $face,y ;copy one byte + sta (.lz_dst),y +.lz_mcopy_len = * + 1 + cpy #$ff + bne .lz_mcopy + + tya ;advance destination pointer +; sec ;XXX TODO carry set = type check needed, cleared (literal) = match follows anyway + adc .lz_dst + sta .lz_dst + +.if BITFIRE_DECOMP_ZERO_OVERLAP = 0 +.lz_skip_poll bcc :+ +.lz_maximum inc .lz_dst+1 ;this is also used by maximum length + bcs .lz_skip_end +: +.else + bcc :+ ;proceed to check +.lz_maximum + inc .lz_dst+1 ;advance hi byte +; lda .lz_dst ;if entering via .lz_maximum, a = 0, so we would pass the following check only if the endadress is @ $xx00 +: ;if so, the endaddress can't be $xx00 and the highbyte check will fail, as we just successfully wrote a literal with type bit, so the end address must be greater then the current lz_dst, as either another literal or match must follow. Can you still follow me?! :-D + eor .lz_end ;check end address +.lz_skip_poll beq .lz_check_end ;all okay, poll for a new block + +.endif ; BITFIRE_DECOMP_ZERO_OVERLAP + + POLLBLOCK + +.lz_skip_end + ;literals needing an explicit type bit + asl .lz_bits ;fetch next type bit + jmp .lz_type_check + +.if BITFIRE_DECOMP_ZERO_OVERLAP + +.lz_check_end + lda .lz_dst+1 ;check highbyte + eor .lz_end+1 + bne .lz_skip_end ;skip poll, so that only one branch needs to be manipulated + ;sta .barrier ;clear barrier and force to load until EOF, XXX does not work, but will at least force one additional block before leaving as barrier will be set again upon next block being fetched. Will overlap be > than 2 blocks? most likely not? CRAP, tony taught me that there is /o\ + ;lda #$ff + ;sta bitfire_load_addr_hi ;needed if the barrier method will not work out, plain jump to poll loop will fail on stand alone depack? + ;jmp .lz_next_page ;load any remaining literal blob if there, or exit with rts in case of plain decomp (rts there instead of php). So we are forced until either the sector_ptr reaches $00xx or EOF happens, so nothing can go wrong + + ; fetching any remaining final literals uncompressed blob is performed by the caller (loadcompd in resident.s) +.endif ; BITFIRE_DECOMP_ZERO_OVERLAP + +.lz_end_of_file + rts + +.lz_refill_bits +bitfire_lz_sector_ptr1 = * + 1 +bitfire_load_addr_hi = * + 2 + ldy $ff00,x + inx + bne .lz_same_page + +.lz_next_page + inc bitfire_load_addr_hi + inc bitfire_lz_sector_ptr2 + 1 +.lz_next_page_ + php + pha + sty .lz_tmp + GETBLOCK bitfire_load_addr_hi + ldx #0 + ldy .lz_tmp + pla + plp + +.lz_same_page + ;store bits? happens on all calls, except when a whole literal is fetched + bcc :+ ;only store lz_bits if carry is set (in all cases, except when literal is fetched for offset) + sty .lz_bits + rol .lz_bits +: rts + +.lz_lentab = * - 1 + ;short offset init values + ;.byte %00000000 ;2 + .byte %11011111 ;0 + .byte %11111011 ;1 + .byte %10000000 ;3 + + ;long offset init values + .byte %11101111 ;offset 0 + .byte %11111101 ;offset 1 + .byte %10000000 ;offset 2 + .byte %11110000 ;offset 3 diff --git a/loader/src/decompress/doynaxdecomp.s b/loader/src/decompress/doynaxdecomp.s new file mode 100755 index 0000000..cf1c0f2 --- /dev/null +++ b/loader/src/decompress/doynaxdecomp.s @@ -0,0 +1,305 @@ + +decompsrc = lz_sector_ptr1 + +;------------------------------------------------------------------------------- +;Regular version of the Lempel-Ziv decompressor +;------------------------------------------------------------------------------- +lz_dst = decdestlo ;Decompression destination pointer. + ;Initialize this to whatever address + ;you want to decompress to + +lz_bits = DECOMPVARS + $00 ;Shift register. Initialized to $80 + ;for a new file + +lz_scratch = DECOMPVARS + $01 ;Temporary zeropage storage + +lz_ybuffer = DECOMPVARS + $02 ;Temporary register storage when fetching sector + +lz_sector = $ff00 ;The one-page buffer from which the + ;compressed data is actually read, + ;and which gets refilled by + ;lz_fetch_sector. + +;------------------------------------------------------------------------------- +;This is the user's hook to replenish the sector buffer with some new bytes. +; +;A and Y are expected to be preserved while carry must remain set on exit. +;X should point to the first byte of the new data, e.g. zero for a full 256-byte +;page of data or two to skip past the sector and track links. +; +;When fetching from a larger in-memory array rather than a single sector buffer +;the lz_sector_ptr1..3 pointers will need to be patched up +;------------------------------------------------------------------------------- +lz_fetch_sector: + inc lz_sector_ptr1 + 1 + inc lz_sector_ptr2 + 1 + inc lz_sector_ptr3 + 1 +lz_fetch_sector_: + pha + sty lz_ybuffer + GETBLOCK lz_sector_ptr1 + 1 + ldx #0 + ldy lz_ybuffer + pla + sec + rts + +decompress: + jsr lz_fetch_sector_ + lda loadaddrhi + sta lz_sector_ptr2 + 1 + sta lz_sector_ptr3 + 1 + ldx loadaddrlo + + jsr _lz_refill_bits + tya +storedadrl: + sta lz_dst + $00 + jsr _lz_refill_bits + tya +storedadrh: + sta lz_dst + $01 + +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc lz_dst + $00 + sta lz_dst + $00 + lda loadaddroffshi + adc lz_dst + $01 + sta lz_dst + $01 +.endif + +;------------------------------------------------------------------------------- +;This is the main lz_decrunch function which may be called to decompress an +;entire file. +; +;On entry and exit the X register points to the next available byte in the +;sector buffer, in ascending order from $00 to $ff. +;This implies that the initial sector must have already been fetched, and that a +;file ending with X wrapped to $00 will have needlessly fetched an extra sector +;(which may be taken advantage of when decoding a contiguous set of files.) +;------------------------------------------------------------------------------- + + ;******** Start the next match/literal run ******** + +lz_decrunch: sec ;This is the main entry point. Forcibly +_lz_type_refill: + jsr _lz_refill_bits ;fill up the the bit buffer on entry + bne _lz_type_cont ;(BRA) + + ;Wrap the high-byte of the destination pointer. +_lz_mfinish: bcc *+4 +_lz_maximum: inc lz_dst+1 ;This is also used by maximum length + ;literals needing an explicit type bit + + POLLBLOCK + + ;Literal or match to follow? + asl lz_bits +_lz_type_cont: bcc _lz_do_match + beq lz_decrunch + + + ;******** Process literal run ******** + + lda #%00000000 ;Decode run length +_lz_lrun_loop: rol + asl lz_bits + bcs _lz_lrun_test +_lz_lrun_back: asl lz_bits + bne _lz_lrun_loop + + jsr _lz_refill_bits + bne _lz_lrun_loop ;(BRA) + +_lz_lrun_test: bne _lz_lrun_gotten + jsr _lz_refill_bits + bcc _lz_lrun_back + +_lz_lrun_gotten: + sta _lz_copy_cnt+1 ;Store LSB of run-length + ldy #$00 +_lz_lcopy: +lz_sector_ptr2 = *+1 ;Copy the literal data. Note the + lda lz_sector,x + inx + bne *+5 + jsr lz_fetch_sector ;Grab a new sector for the literal loop + sta (lz_dst),y + iny +_lz_copy_cnt: cpy #$00 + bne _lz_lcopy + + ;Time to advance the destination pointer. + ;Maximum run length literals exit here as a type-bit needs + ;to be fetched afterwards + tya + beq _lz_maximum + clc + adc lz_dst+0 + sta lz_dst+0 + bcc *+4 + inc lz_dst+1 + + POLLBLOCK + + ;One literal run following another only makes sense if the + ;first run is of maximum length and had to be split. As that + ;case has been taken care of we can safely omit the type bit + ;here + + + ;******** Process match ******** + +_lz_do_match: lda #%00100000 ;Determine offset length by a two-bit +_lz_moff_range: asl lz_bits ;prefix combined with the first run + bne *+5 ;length bit (where a one identifies + jsr _lz_refill_bits ;a two-byte match). + rol ;The rest of the length bits will + bcc _lz_moff_range ;then follow *after* the offset data + + tay + lda _lz_moff_length,y + beq _lz_moff_far + +_lz_moff_loop: asl lz_bits ;Load partial offset byte + bne *+9 + sty lz_scratch + jsr _lz_refill_bits + ldy lz_scratch + + rol + bcc _lz_moff_loop + + bmi _lz_moff_near + +_lz_moff_far: sta lz_scratch ;Save the bits we just read as the + ;high-byte + +lz_sector_ptr3 = *+1 + lda lz_sector,x ;For large offsets we can load the + inx ;low-byte straight from the stream + bne *+5 ;without going throught the shift + jsr lz_fetch_sector ;register + +; sec + adc _lz_moff_adjust_lo,y + bcs _lz_moff_pageok + dec lz_scratch + sec +_lz_moff_pageok: + adc lz_dst+0 + sta _lz_match+0 + + lda lz_scratch + adc _lz_moff_adjust_hi,y + sec + bcs _lz_moff_join ;(BRA) + +_lz_moff_near: +; sec ;Special case handling of <8 bit offsets. + adc _lz_moff_adjust_lo,y;We may can safely ignore the MSB from +; sec ;the base adjustment table as the + adc lz_dst+0 ;maximum base (for a 4/5/6/7 bit + sta _lz_match+0 ;length sequence) is 113 + lda #$ff +_lz_moff_join: adc lz_dst+1 + sta _lz_match+1 + + cpy #$04 ;Get any remaning run length bits + lda #%00000001 + bcs _lz_mrun_gotten + +_lz_mrun_loop: asl lz_bits + bne *+5 + jsr _lz_refill_bits + rol + asl lz_bits + bcc _lz_mrun_loop + bne _lz_mrun_gotten + jsr _lz_refill_bits + bcc _lz_mrun_loop + +_lz_mrun_gotten: + tay ;A 257-byte (=>$00) run serves as a + beq _lz_end_of_file ;sentinel + + sta _lz_mcopy_len + + ldy #$ff ;The copy loop. This needs to be run +_lz_mcopy: iny ;forwards since RLE-style matches can +_lz_match = *+1 ;overlap the destination + lda $ffff,y + sta (lz_dst),y +_lz_mcopy_len = *+1 + cpy #$ff + bne _lz_mcopy + + tya ;Advance destination pointer +; sec + adc lz_dst+0 + sta lz_dst+0 + jmp _lz_mfinish + + + ;******** Fetch some more bits to work with ******** + +lz_sector_ptr1 = *+1 +_lz_refill_bits: + ldy lz_sector,x + sty lz_bits + inx + bne *+5 + jsr lz_fetch_sector +; sec + rol lz_bits +_lz_end_of_file: + rts + + ;******** Offset coding tables ******** + + ;This length table is a bit funky. The idea here is to use the + ;value as the initial value of the shift register instead of + ;keeping a separate counter. + ;In other words we iterate until the leading one is shifted out. + ;Then afterwards the bit just below it (our new sign bit) is set + ;if the offset is shorter than 8-bits, and conversely it's + ;cleared if we need to fetch a separate low-byte + ;as well. + ;The fact that the sign bit is cleared as a flag is compensated + ;for in the lz_moff_adjust_hi table + +_lz_moff_length: + ;Long (>2 byte matches) + .byte %00011111 ;4 bits + .byte %00000011 ;7 bits + .byte %01011111 ;10 bits + .byte %00001011 ;13 bits + ;Short (2 byte matches) + .byte %01011111 ;10 bits + .byte %00000000 ;8 bits + .byte %00000111 ;6 bits + .byte %00111111 ;3 bits +_lz_moff_adjust_lo: + ;Long (>2 byte matches) + .byte %11111110 ;1-16 + .byte %11101110 ;17-144 + .byte %01101110 ;145-1168 + .byte %01101110 ;1169-9360 + ;Short (2 byte matches) + .byte %10110110 ;329-1352 + .byte %10110110 ;73-328 + .byte %11110110 ;9-72 + .byte %11111110 ;1-8 +_lz_moff_adjust_hi = *-2 + ;Long (>2 byte matches) +; .byte %11111111 ;1-16 (unreferenced) +; .byte %11111111 ;17-144 (unreferenced) + .byte %01111111 ;145-1168 + .byte %01111011 ;1169-9360 + ;Short (2 byte matches) + .byte %01111110 ;329-1352 + .byte %11111110 ;73-328 +; .byte %11111111 ;9-72 (unreferenced) +; .byte %11111111 ;1-8 (unreferenced) diff --git a/loader/src/decompress/exodecomp.s b/loader/src/decompress/exodecomp.s new file mode 100755 index 0000000..261c26f --- /dev/null +++ b/loader/src/decompress/exodecomp.s @@ -0,0 +1,611 @@ +; slightly modified by Krill/Plush for loader integration + +; +; Copyright (c) 2002 - 2020 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; Known quirks: +; Can't handle a sequence reference that ends at $ffff. It is left in +; since it is a corner case and fixing it impacts negatively on +; performance or backwards compatibility. +; A simple way to work around this is to not decrunch to address $ffff. +; ------------------------------------------------------------------- +; Controls if the shared get_bits routines should be inlined or not. +;INLINE_GET_BITS=1 +.IFNDEF INLINE_GET_BITS +INLINE_GET_BITS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if literal sequences is not used (the data was crunched with the -c +; flag) then the following line can be uncommented for shorter and. +; slightly faster code. +;LITERAL_SEQUENCES_NOT_USED = 1 +.IFNDEF LITERAL_SEQUENCES_NOT_USED +LITERAL_SEQUENCES_NOT_USED = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length is limited to 256 (the data was crunched with +; the -M256 flag) then the following line can be uncommented for +; shorter and slightly faster code. +;MAX_SEQUENCE_LENGTH_256 = 1 +.IFNDEF MAX_SEQUENCE_LENGTH_256 +MAX_SEQUENCE_LENGTH_256 = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length 3 has its own offset table (the data was +; crunched with the -P+16 flag) then the following +; line must be uncommented. +;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1 +.IFNDEF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE +EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0 +.ENDIF +; ------------------------------------------------------------------- +; if sequence offsets are not reused (the data was crunched with the +; -P-32 flag) then the following line must be uncommented. Uncommenting the +; line will also result in shorter and slightly faster code. +;DONT_REUSE_OFFSET = 1 +.IFNDEF DONT_REUSE_OFFSET +DONT_REUSE_OFFSET = 0 +.ENDIF +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +DECRUNCH_FORWARDS = 1 +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if split encoding is used (the data is crunched with the -E flag) +; then the following line must be uncommented. +;ENABLE_SPLIT_ENCODING = 1 +.IFNDEF ENABLE_SPLIT_ENCODING +ENABLE_SPLIT_ENCODING = 0 +.ENDIF + +; ------------------------------------------------------------------- +; The decruncher jsr:s to the get_crunched_byte address when it wants to +; read a crunched byte into A. This subroutine has to preserve X and Y +; register and must not modify the state of the carry nor the overflow flag. +; ------------------------------------------------------------------- +;.import get_crunched_byte +; ------------------------------------------------------------------- +; This function is the heart of the decruncher. (for non split crunched files) +; It initializes the decruncher zeropage locations and precalculates the +; decrunch tables and decrunches the data +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- +;.export decrunch +.IF ENABLE_SPLIT_ENCODING <> 0 +; ------------------------------------------------------------------- +; To decrunch files crunched with the split feature (-E) you can't use the +; decrunch function. Instead you call the split_decrunch function. But you +; can only do this if the decrunch table contains the encoding used by the +; file you are decrunching. To generate the correct content for the decrunch +; table call set the get_crunched_byte function to point to the encoding data +; and then call the split_gentable function. +; ------------------------------------------------------------------- +.export split_gentable +.export split_decrunch +.ENDIF +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +zp_len_lo = DECOMPVARS + 0; $9e +zp_len_hi = DECOMPVARS + 1; $9f + +zp_src_lo = DECOMPVARS + 2; $ae +zp_src_hi = zp_src_lo + 1 + +zp_bits_hi = DECOMPVARS + 4; $a7 +.IF DONT_REUSE_OFFSET = 0 +zp_ro_state = DECOMPVARS + 5; $a8 +.ENDIF + +zp_bitbuf = DECOMPVARS + 6; $fd +.if MEM_DECOMP_TO_API +zp_dest_lo = decdestlo; zp_bitbuf + 1 ; dest addr lo +zp_dest_hi = decdesthi; zp_bitbuf + 2 ; dest addr hi +.else +zp_dest_lo = zp_bitbuf + 1 ; dest addr lo +zp_dest_hi = zp_bitbuf + 2 ; dest addr hi +.endif + +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +encoded_entries = 68 +.ELSE +encoded_entries = 52 +.ENDIF + +tabl_bi = decrunch_table +tabl_lo = decrunch_table + encoded_entries +tabl_hi = decrunch_table + encoded_entries * 2 + +decompress = decrunch + +get_crunched_byte: + php + jsr getbyte + plp + rts + + ;; refill bits is always inlined +.MACRO mac_refill_bits + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +.ENDMACRO + +.MACRO mac_get_bits +.IF INLINE_GET_BITS <> 0 +.SCOPE + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvc skip +gb_get_hi: + sec + sta zp_bits_hi + jsr get_crunched_byte +skip: +.ENDSCOPE +.ELSE + jsr get_bits +.ENDIF +.ENDMACRO + +.MACRO mac_init_zp +;.SCOPE +; ------------------------------------------------------------------- +; init zeropage and x reg. (8 bytes) +; +init_zp: +.if MEM_DECOMP_TO_API + jsr get_crunched_byte +storedadrh: + sta zp_dest_hi + jsr get_crunched_byte +storedadrl: + sta zp_dest_lo + jsr get_crunched_byte + sta zp_bitbuf +.else + jsr get_crunched_byte + sta zp_bitbuf - 1,x + dex + bne init_zp +.endif + +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc zp_dest_lo + sta zp_dest_lo + lda loadaddroffshi + adc zp_dest_hi + sta zp_dest_hi +.endif +;.ENDSCOPE +.ENDMACRO + +.IF INLINE_GET_BITS = 0 +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvs gb_get_hi + rts +gb_get_hi: + sec + sta zp_bits_hi + jmp get_crunched_byte +.ENDIF +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to decrunch, it will in turn init the tables and +; call the decruncher +; no constraints on register content, however the +; decimal flag has to be cleared (it almost always is, otherwise do a cld) +decrunch: +.IF ENABLE_SPLIT_ENCODING <> 0 + ldx #3 + jsr internal_gentable + jmp normal_decrunch +split_gentable: + ldx #1 +internal_gentable: + jsr split_init_zp +.ELSE + ldx #3 + mac_init_zp +.ENDIF +; ------------------------------------------------------------------- +; calculate tables (64 bytes) + get_bits macro +; x must be #0 when entering +; + ldy #0 + clc +table_gen: + tax + tya + and #$0f + sta tabl_lo,y + beq shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc tabl_lo - 1,y + sta tabl_lo,y + lda zp_len_hi + adc tabl_hi - 1,y +shortcut: + sta tabl_hi,y +; ------------------------------------------------------------------- + lda #$01 + sta 0 + rts +split_decrunch: + ldx #3 + jsr split_init_zp +; X reg must be 0 here + sec +normal_decrunch: +.ENDIF +; ------------------------------------------------------------------- +; prepare for main decruncher +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state + sec +.ENDIF + ldy zp_dest_lo + stx zp_dest_lo + stx zp_bits_hi +; ------------------------------------------------------------------- +; copy one literal byte to destination (11 bytes) +; +literal_start1: +.IF DECRUNCH_FORWARDS = 0 + tya + bne no_hi_decr + dec zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + dec zp_src_hi +.ENDIF +no_hi_decr: + dey +.ENDIF + jsr get_crunched_byte + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne skip_hi_incr + inc zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + inc zp_src_hi +.ENDIF +skip_hi_incr: +.ENDIF +; ------------------------------------------------------------------- +; fetch sequence length index (15 bytes) +; x must be #0 when entering and contains the length index + 1 +; when exiting or 0 for literal byte +next_round: +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state +.ENDIF + dex + lda zp_bitbuf +no_literal1: + asl + bne nofetch8 + jsr get_crunched_byte + rol +nofetch8: + inx + bcc no_literal1 + sta zp_bitbuf +; ------------------------------------------------------------------- +; check for literal byte (2 bytes) +; + beq literal_start1 +; ------------------------------------------------------------------- +; check for decrunch done and literal sequences (4 bytes) +; + cpx #$11 +.IF INLINE_GET_BITS <> 0 + bcc skip_jmp + jmp exit_or_lit_seq +skip_jmp: +.ELSE + bcs exit_or_lit_seq +.ENDIF +; ------------------------------------------------------------------- +; calulate length of sequence (zp_len) (18(11) bytes) + get_bits macro +; + lda tabl_bi - 1,x + mac_get_bits + adc tabl_lo - 1,x ; we have now calculated zp_len_lo + sta zp_len_lo +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_bits_hi + adc tabl_hi - 1,x ; c = 0 after this. + sta zp_len_hi +; ------------------------------------------------------------------- +; here we decide what offset table to use (27(26) bytes) + get_bits_nc macro +; z-flag reflects zp_len_hi here +; + ldx zp_len_lo +.ELSE + tax +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda #0 +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; here we decide to reuse latest offset or not (13(15) bytes) +; + bit 0 + cpx #$04 +.ELSE + cpx #$03 +.ENDIF + bcs gbnc2_next + lda tabl_bit - 1,x +gbnc2_next: + asl zp_bitbuf + bne gbnc2_ok + tax + jsr get_crunched_byte + rol + sta zp_bitbuf + txa +gbnc2_ok: + rol + bcs gbnc2_next + tax +; ------------------------------------------------------------------- +; calulate absolute offset (zp_src) (17 bytes) + get_bits macro +; + lda tabl_bi,x + mac_get_bits +.IF DECRUNCH_FORWARDS = 0 + adc tabl_lo,x + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + adc zp_dest_hi + sta zp_src_hi +.ELSE + clc + adc tabl_lo,x + eor #$ff + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + eor #$ff + adc zp_dest_hi + sta zp_src_hi + clc +.ENDIF +; ------------------------------------------------------------------- +; prepare for copy loop (2 bytes) +; + ldx zp_len_lo +; ------------------------------------------------------------------- +; main copy loop (30 bytes) +; +copy_next: +.IF DECRUNCH_FORWARDS = 0 + tya + bne copy_skip_hi + dec zp_dest_hi + dec zp_src_hi +copy_skip_hi: + dey +.ENDIF +.IF LITERAL_SEQUENCES_NOT_USED = 0 + bcs get_literal_byte +.ENDIF + lda (zp_src_lo),y +literal_byte_gotten: + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne copy_skip_hi + inc zp_dest_hi + inc zp_src_hi +copy_skip_hi: +.ENDIF + dex + bne copy_next +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_len_hi +.IF INLINE_GET_BITS <> 0 + bne copy_next_hi +.ENDIF +.ENDIF + stx zp_bits_hi +.IF INLINE_GET_BITS = 0 + beq next_round +.ELSE + jmp next_round +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 +copy_next_hi: + dec zp_len_hi + jmp copy_next +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; test for offset reuse (11 bytes) +; +test_reuse: + bvs no_reuse +.IF MAX_SEQUENCE_LENGTH_256 <> 0 + lda #$00 ; fetch one bit +.ENDIF + asl zp_bitbuf + bne gbnc1_ok + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +gbnc1_ok: + rol + beq no_reuse ; bit == 0 => C=0, no reuse + bne copy_next ; bit != 0 => C=0, reuse previous offset +.ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +exit_or_lit_seq: +.IF LITERAL_SEQUENCES_NOT_USED = 0 + beq decr_exit + jsr get_crunched_byte +.IF MAX_SEQUENCE_LENGTH_256 = 0 + sta zp_len_hi +.ENDIF + jsr get_crunched_byte + tax + bcs copy_next +decr_exit: +.ENDIF + rts +.IF LITERAL_SEQUENCES_NOT_USED = 0 +get_literal_byte: + jsr get_crunched_byte + bcs literal_byte_gotten +.ENDIF +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes) +; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to +; %10010000, %11100011, %11100010 +tabl_bit: + .BYTE $90, $e3, $e2 +.ELSE +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1 and 2 (2 bytes) +; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010 +tabl_bit: + .BYTE $8c, $e2 +.ENDIF + +.IF ENABLE_SPLIT_ENCODING <> 0 +split_init_zp: + mac_init_zp + rts +.ENDIF +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; this 156 (204) byte table area may be relocated. It may also be +; clobbered by other data between decrunches. +; ------------------------------------------------------------------- +decrunch_table: + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.ENDIF + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0 +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- diff --git a/loader/src/decompress/lcdecomp.s b/loader/src/decompress/lcdecomp.s new file mode 100755 index 0000000..671b9a7 --- /dev/null +++ b/loader/src/decompress/lcdecomp.s @@ -0,0 +1,180 @@ + +; the following depack code is +; written by Marek Matula (MMS/Taboo) +; in its original form and has been +; slightly modified + +; compress using taboo levelcrush or +; crush.exe by taboo, +; don't forget to convert the +; compressed files (not needed for mem-decompressing) + + +; Level-crusher v1.0/v1.1 depacker +; (c)1998 Taboo Productions! +; All rights reserved + +LC_SPEED = 6 + +byte = DECOMPVARS+$00 +hi = DECOMPVARS+$01 +dest = decdestlo +desth = decdesthi + +decompress: + jsr getbyte +.if LOADCOMPD_TO + clc + adc loadaddroffslo + php +.endif +storedadrl: sta dest + jsr getbyte +.if LOADCOMPD_TO + plp + adc loadaddroffshi +.endif +storedadrh: sta desth + + ldx #$00 + stx byte +jp18: stx hi + lda #$01 + asl byte + bne *+$05 + jsr getbit + bcs jp2 +jp4: asl byte + bne *+$05 + jsr getbit + bcs jp3 + asl byte + bne *+$05 + jsr getbit + rol a + rol hi + bpl jp4 +jp3: tax + beq jp5 + + ; literal run + ldy #$00 +literalrun: jsr getbyte + sta (dest),y + inc dest + bne :+ + inc desth +: dex + bne literalrun +jp5: cpx hi + dec hi + bcc literalrun + stx hi + +jp2: lda #$01 + asl byte + bne *+$05 + jsr getbit + bcc jp9 +jp8: asl byte + bne *+$05 + jsr getbit + bcs jp10 + asl byte + bne *+$05 + jsr getbit + rol a + bcc jp8 + + ; decompression finished + rts + +jp9: inx +jp10: adc #$01 + sta depseqle + txa + asl byte + bne *+$05 + jsr getbit + rol a + asl byte + bne *+$05 + jsr getbit + rol a + tay + lda #$00 +jp12: ldx tab,y +jp11: asl byte + bne *+$05 + jsr getbit + rol a + rol hi + dex + bne jp11 + dey + bmi jp14 + cpy #$03 + clc + beq jp14 + adc #$01 + bcc jp12 + inc hi + bcs jp12 +jp14: adc depseqle + bcc jp15 + inc hi + + ; copy sequence +jp15: clc + sbc dest + eor #$ff + sta depseqcp+$01 + lda hi + sbc desth + eor #$ff + sta depseqcp+$02 + ldy #$00 +depseqcp: lda $00,y + sta (dest),y + iny +depseqle = *+$01 + cpy #$00 + bne depseqcp + tya + clc + adc dest + sta dest + bcc jp17 + inc desth +jp17: jmp jp18 + +getbit: pha + jsr getbyte + sec + rol a + sta byte + pla +jp19: rts + +tab: + .if LC_SPEED = 6 + .byte 4,3,3,3,4,2,2,2 + .endif + .if LC_SPEED = 5 + .byte 4,2,3,3,4,2,2,2 + .endif + .if LC_SPEED = 4 + .byte 4,2,2,3,4,2,2,2 + .endif + .if LC_SPEED = 3 + .byte 4,2,2,2,4,2,2,2 + .endif + .if LC_SPEED = 2 + .byte 3,2,2,2,3,2,2,2 + .endif + .if LC_SPEED = 1 + .byte 3,1,2,2,3,1,2,2 + .endif + .if LC_SPEED = 0 + .byte 2,2,1,1,2,2,1,1 + .endif diff --git a/loader/src/decompress/lzsa2decomp.s b/loader/src/decompress/lzsa2decomp.s new file mode 100644 index 0000000..7451334 --- /dev/null +++ b/loader/src/decompress/lzsa2decomp.s @@ -0,0 +1,334 @@ + +; with slight modifications by Krill/Plush + +.FEATURE labels_without_colons, leading_dot_in_identifiers + +decompress = .loadcomp_entry +decompsrc = lzsa_srcptr + +.if MEM_DECOMP_TO_API + ; cannot copy remaining uncompressed blob of unknown size + .error "***** MEM_DECOMP_TO_API is not supported for LZSA2. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****" +.endif + +BITFIRE_ZP_ADDR = DECOMPVARS - 1 + +.define asr alr + +lzsa2_get_byte: + lda (lzsa_srcptr),y ;Subroutine version for when + inc no literal, continue with match + + cmp #$03 ;Extended length? + bne .got_cp_len ;1..2 + jsr .get_length ;x is set by .get_length + beq .put_cp_len +.got_cp_len + inx ;Increment # of pages to copy. +.put_cp_len + stx fetch a lowbyte + ;9 bit fetch a bit + ;16 -> fetch a highbyte + ;rep, skip all + +.lz_offset + lda $ff/$fe + rol + bne .get_low8 ;BRA +.get_5_13_bits + sta adc #$0f + sta $10 + lzsa_tmp +.got_length ;Z-flag is set + rts ;lengths. + +.byte_length + jsr lzsa2_get_byte ;So rare, this can be slow! + clc ;adc #$0f + 0 + adc 10101010 -> first nibble = and #$55, second nibble = asr #$aa? +lzsa2_get_nibble + lsr LZ77 + ;tya ; A = 0 + jsr get1bit ; X = 0 + lsr ; bit -> C, A = 0 + bcc lz77_2 ; A=0 -> LZPOS+1 + ;***FALL THRU*** + + ; e..e01 + jsr get1bit ; X = 0 + lsr ; bit -> C, A = 0 + bcc newesc ; e..e010 + ;***FALL THRU*** + + ; e..e011 +srle: iny ; Y is 1 bigger than MSB loops + jsr getval ; Y is 1, get len, X = 0 + sta LZPOS ; xstore - Save length LSB +mg1: cmp #64 ; ** PARAMETER 63-64 -> C clear, 64-64 -> C set.. + bcc chrcode ; short RLE, get bytecode + +longrle: ldx #2 ; ** PARAMETER 111111xxxxxx + jsr getbits ; get 3/2/1 more bits to get a full byte, X = 0 + sta LZPOS ; xstore - Save length LSB + + jsr getval ; length MSB, X = 0 + tay ; Y is 1 bigger than MSB loops + +chrcode: jsr getval ; Byte Code, X = 0 + tax ; this is executed most of the time anyway + lda table-1,x; Saves one jump if done here (loses one txa) + +.if OLD_VERSION + cpx #32 ; 31-32 -> C clear, 32-32 -> C set.. +.else + cpx #16 ; 15-16 -> C clear, 16-16 -> C set.. +.endif + bcc @1 ; 1..31, we got the right byte from the table + + ; Ranks 32..64 (11111°xxxxx), get byte.. + txa ; get back the value (5 valid bits) +.if OLD_VERSION + ldx #3 +.else + ldx #4 +.endif + jsr getbits ; get 3 more bits to get a full byte, X = 0 + +@1: ldx LZPOS ; xstore - get length LSB + inx ; adjust for cpx#$ff;bne -> bne +dorle: jsr putch + dex + bne dorle ; xstore 0..255 -> 1..256 + dey + bne dorle ; Y was 1 bigger than wanted originally +mainbeq: beq main ; reverse condition -> jump always + + +lz77: jsr getval ; X = 0 +mg21: cmp #127 ; ** PARAMETER Clears carry (is maximum value) + bne noeof + ; EOF +eof: +hi: ldx #0 +lo: ldy #0 + rts + +noeof: sbc #0 ; C is clear -> subtract 1 (1..126 -> 0..125) +elzpb: ldx #0 ; ** PARAMETER (more bits to get) + jsr getchkf ; clears Carry, X = 0 +lz77_2: sta LZPOS+1 ; offset MSB + jsr get8bit ; clears Carry, X = 0 + ; Note: Already eored in the compressor.. + ;eor #255 ; offset LSB 2's complement -1 (i.e., -X = ~X+1) + adc OUTPOS ; -offset -1 + curpos (C is clear) + ldx LZPOS ; xstore = LZLEN (read before it's overwritten) + sta LZPOS + + lda OUTPOS+1 + sbc LZPOS+1 ; takes C into account + sta LZPOS+1 ; copy X+1 number of chars from LZPOS to OUTPOS + ;ldy #0 ; Y was 0 originally, we don't change it + + inx ; adjust for cpx#$ff;bne -> bne +lzloop: lda (LZPOS),y; using abs,y is 3 bytes longer, only 1 cycle/byte faster + jsr putch ; Note: must be copied forwards! + iny ; Y does not wrap because X=0..255 and Y initially 0 + dex + bne lzloop ; X loops, (256,1..255) + beq mainbeq ; jump through another beq (-1 byte, +3 cycles) + + +getnew: pha ; 1 Byte/3 cycles + jsr getbyte + sec + rol ; Shift out the next bit and + ; shift in C=1 (last bit marker) + sta bitstr ; bitstr initial value = $80 == empty + pla ; 1 Byte/4 cycles + rts + ; 25+12 = 37 + + ; getval : Gets a 'static huffman coded' value + ; ** Scratches X, returns the value in A ** +getval: inx ; X <- 1 + txa ; set the top bit (value is 1..255) +gv0: asl bitstr + bne @1 + jsr getnew +@1: bcc getchk ; got 0-bit + inx +mg: cpx #7 ; ** PARAMETER unary code maximum length + 1 + bne gv0 + beq getchk ; inverse condition -> jump always + ; getval: 18 bytes + ; 15 + 17*n + 6+15*n+12 + 36*n/8 = 33 + 32*n + 36*n/8 cycles + + ; getbits: Gets X bits from the stream + ; ** Scratches X, returns the value in A ** +get8bit: ldx #7 +get1bit: inx ;2 +getbits: asl bitstr + bne @1 + jsr getnew +@1: rol ;2 +getchk: dex ;2 more bits to get ? +getchkf: bne getbits ;2/3 + clc ;2 return carry cleared + rts ;6+6 + +OUTPOS = *+$01 +putch: sta $aaaa ; ** parameter + inc OUTPOS ; ZP + bne @0 + inc OUTPOS+1 ; ZP +@0: rts + +.if OLD_VERSION +table: .res 31,0 +.else +table: .res 15,0 +.endif diff --git a/loader/src/decompress/subsizerdecomp.s b/loader/src/decompress/subsizerdecomp.s new file mode 100755 index 0000000..601c14a --- /dev/null +++ b/loader/src/decompress/subsizerdecomp.s @@ -0,0 +1,491 @@ + +;************************************************************************** +;* +;* Copyright (c) 2015, 2017 Daniel Kahlin +;* Written by Daniel Kahlin +;* Slightly modified by Gunnar Ruthenberg +;* +;* DESCRIPTION +;* subsizer 0.6 decruncher - stand alone version +;* +;* usage: +;* You need to provide a function to get a byte from the input +;* stream. (must preserve X,Y and C) +;* +;****** + +decompress = decrunch + +.FEATURE leading_dot_in_identifiers + +;************************************************************************** +;* +;* Configuration options +;* +;****** +FORWARD_DECRUNCHING = 1 +HAVE_LONG_PARTS = 1 + + + .if HAVE_LONG_PARTS +PART_MASK = %00001111 +N_PARTS = 16 + .else +PART_MASK = %00000111 +N_PARTS = 8 + .endif + + +len_zp = DECOMPVARS +copy_zp = DECOMPVARS + 1 +hibits_zp = DECOMPVARS + 3 +buf_zp = DECOMPVARS + 4 +.if MEM_DECOMP_TO_API +dest_zp = decdestlo +.else +dest_zp = DECOMPVARS + 5 +.endif +endm_zp = DECOMPVARS + 7 + + +;************************************************************************** +;* +;* NAME fast macros +;* +;****** + +;****** +;* get bit macro + .macro get_bit + .local .gb_skp1 + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + jsr dc_get_byte + rol + sta buf_zp +.gb_skp1: + .endmacro + + +;****** +;* get bits max8 macro + .macro get_bits_max8 + .local .gb_lp1 + .local .gb_skp1 +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta buf_zp + pla +.gb_skp1: + rol + dey + bne .gb_lp1 + .endmacro + + +;****** +;* get bits max8 masked macro + .macro get_bits_max8_masked + .local .gb_lp1 + .local .gb_skp1 +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta buf_zp + tya +.gb_skp1: + rol + bcs .gb_lp1 + .endmacro + + +;****** +;* get bits max16 macro + .macro get_bits_max16 + .local .gb_lp1 + .local .gb_skp1 +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta buf_zp + pla +.gb_skp1: + rol + rol hibits_zp + dey + bne .gb_lp1 ; C=0 for all Y!=0 + .endmacro + + +;************************************************************************** +;* +;* NAME decrunch +;* +;****** +decrunch: +.if MEM_DECOMP_TO_API +; Get endm_zp, dest_zp, and buf_zp +dc_lp00: + jsr dc_get_byte + sta endm_zp + jsr dc_get_byte +storedadrh: + sta dest_zp+1 + jsr dc_get_byte +storedadrl: + sta dest_zp+0 + jsr dc_get_byte + sta buf_zp + ldx #0 +.else + ldx #4 +dc_lp00: + jsr dc_get_byte + sta buf_zp-1,x + dex + bne dc_lp00 +.endif +; X = 0 + +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc dest_zp+0 + sta dest_zp+0 + lda loadaddroffshi + adc dest_zp+1 + sta dest_zp+1 +.endif + +; ldx #0 +dc_lp01: + +;****** +;* get 4 bits + lda #%11100000 +dcg_lp1: + asl buf_zp + bne dcg_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta buf_zp + tya +dcg_skp1: + rol + bcs dcg_lp1 +; Acc = 4 bits. + + sta bits,x + + txa + and #PART_MASK + tay + beq dc_skp01 + + lda #0 + sta hibits_zp + ldy bits-1,x + sec +dc_lp02: + rol + rol hibits_zp + dey + bpl dc_lp02 +; C = 0 +; clc + adc base_l-1,x + tay + lda hibits_zp + adc base_h-1,x + +dc_skp01: + sta base_h,x + tya + sta base_l,x + inx + cpx #N_PARTS*4+4 + bne dc_lp01 + +; perform decrunch + + ldy #0 + ; fall through + +;************************************************************************** +;* +;* NAME decruncher +;* +;* DESCRIPTION +;* decruncher +;* +;****** +decrunch_entry: + +;****** +;* single literal byte +;* +dc_literal: +.if FORWARD_DECRUNCHING + jsr dc_get_byte +; ldy #0 + sta (dest_zp),y + inc dest_zp + bne dc_skp5 + inc dest_zp+1 +dc_skp5: +dc_common: +.else + lda dest_zp + bne dc_skp5 + dec dest_zp+1 +dc_skp5: + dec dest_zp + jsr dc_get_byte +; ldy #0 +dc_common: + sta (dest_zp),y +.endif + ; fall through + +decrunch_main: + +;------ +; perform actual decrunch +dc_lp1: + get_bit + bcs dc_literal + +; get length as bits/base. + ldx #$80-N_PARTS +dc_lp2: + inx + bmi dc_skp0 + get_bit + bcc dc_lp2 + clc +dc_skp0: +; C = 0, Y = 0 +; lda #0 + tya + ldy bits_len-$80+N_PARTS-1,x + beq dcb1_skp2 + get_bits_max8 +dcb1_skp2: +; C = 0 + adc base_len-$80+N_PARTS-1,x + sta len_zp +; C = 0 + + +.if FORWARD_DECRUNCHING + tax +.else +;****** +;* IN: len = $01..$100 (Acc = $00..$ff) +;* OUT: dest_zp = dest_zp - len, X = len-1 +;* + tax +; clc + eor #$ff + adc dest_zp + sta dest_zp + bcs dc_skp22 + dec dest_zp+1 +dc_skp22: +.endif + +; check end marker here to avoid thrashing carry earlier + cpx endm_zp + beq done + +;****** +;* Get selector bits depending on length. +;* +;* IN: len = $01..$100 (X = $00..$ff) +;* OUT: +;* + cpx #4 + bcc dc_skp2 + ldx #3 +dc_skp2: + +; get offset as bits/base. + lda tabb,x + get_bits_max8_masked + tax +; C = 0 + + lda #0 + sta hibits_zp + ldy bits_offs,x + beq dcb3_skp2 + get_bits_max16 +dcb3_skp2: +; C = 0, Acc/hibits_zp + base_offs,x = offset - 1 + +.if FORWARD_DECRUNCHING + adc base_offs_l,x + bcc dcb3_skp3 + inc hibits_zp + clc +dcb3_skp3: + eor #$ff + adc dest_zp + sta copy_zp + lda dest_zp+1 + sbc hibits_zp + sbc base_offs_h,x + sta copy_zp+1 +.else +; perform: copy_zp = Acc/hibits_zp + base_offs,x + 1 + dest_zp +; result: copy_zp = dest_zp + offset + adc base_offs_l,x + bcc dcb3_skp3 + inc hibits_zp +dcb3_skp3: + sec + adc dest_zp + sta copy_zp + lda hibits_zp + adc base_offs_h,x +; C = 0 + adc dest_zp+1 + sta copy_zp+1 +.endif + +.if FORWARD_DECRUNCHING +copy: +.if 1 +; this is shorter + ldx len_zp + inx + ldy #$ff +dc_lp4: + iny + lda (copy_zp),y + sta (dest_zp),y + dex + bne dc_lp4 +.else +; and this might be faster on average, as there are many 1-byte sequences + ldy len_zp + beq dc_skp4 + ldx len_zp + ldy #$00 +dc_lp4: + lda (copy_zp),y + sta (dest_zp),y + iny + dex + bne dc_lp4 +dc_skp4: + lda (copy_zp),y + sta (dest_zp),y +.endif +; C = 1 + tya + adc dest_zp + sta dest_zp + bcc dc_skp22 + inc dest_zp+1 +dc_skp22: + ldy #$00 +.else +;****** +;* Reverse fast copy +;* +;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0 +;* +copy: + ldy len_zp + beq dc_skp4 +dc_lp4: + lda (copy_zp),y + sta (dest_zp),y + dey + bne dc_lp4 +dc_skp4: + lda (copy_zp),y +; sta (dest_zp),y +.endif + + jmp dc_common +; bcc dc_common ; always taken + +;****** +;* exit out +done: + rts + + .if HAVE_LONG_PARTS +tabb: + .byte %10000000 | (48 >> 2) ; 2 bits + .byte %11100000 | (0 >> 4) ; 4 bits + .byte %11100000 | (16 >> 4) ; 4 bits + .byte %11100000 | (32 >> 4) ; 4 bits + .else +tabb: + .byte %10000000 | (24 >> 2) ; 2 bits + .byte %11000000 | (0 >> 3) ; 3 bits + .byte %11000000 | (8 >> 3) ; 3 bits + .byte %11000000 | (16 >> 3) ; 3 bits + .endif + + + +;************************************************************************** +;* +;* NAME dc_get_byte +;* +;* DESCRIPTION +;* Get byte from the packed stream. +;* +;****** +dc_get_byte = getbyte + +end_decruncher: + +begin_tables: +;************************************************************************** +;* +;* NAME base_l, base_h, bits +;* +;* DESCRIPTION +;* Data for bits/base decoding. +;* +;****** +base_l: +base_len: + .res N_PARTS,0 +base_offs_l: + .res N_PARTS*3+4,0 +base_h = * - N_PARTS +; .res N_PARTS,0 +base_offs_h: + .res N_PARTS*3+4,0 + +bits: +bits_len: + .res N_PARTS,0 +bits_offs: + .res N_PARTS*3+4,0 + +end_tables: + +; eof diff --git a/loader/src/decompress/tcdecomp.s b/loader/src/decompress/tcdecomp.s new file mode 100644 index 0000000..1c21e6f --- /dev/null +++ b/loader/src/decompress/tcdecomp.s @@ -0,0 +1,190 @@ + +decompress = decrunch +decompsrc = sp + +TC_BLOCK_INTERFACE = 1 + +.if (PLATFORM = diskio::platform::COMMODORE_64) | (PLATFORM = diskio::platform::COMMODORE_128) +USE_UNINTENDED_OPCODES = 1 +.else +USE_UNINTENDED_OPCODES = 0 +.endif + + ;.export decrunch +.if USE_UNINTENDED_OPCODES + .define sbx axs +.endif + +.if MEM_DECOMP_TO_API +dp=decdestlo +.else +dp=DECOMPVARS + 0 ;4 +.endif +sp=DECOMPVARS + 2 ;6 ; sp must follow dp, cf init code +cs=DECOMPVARS + 4 ;8 + + +decrunch: +.if TC_BLOCK_INTERFACE = 0 + stx sp+1 +.endif + + ldy#2 +init_loop: +.if USE_UNINTENDED_OPCODES + stx sp-2,y ;first iter stores sp-low :D +.else + sta sp-2,y ;first iter stores sp-low :D +.endif +.if TC_BLOCK_INTERFACE + ; read three blocks ahead, + ; - one because literal strings read up to 128 bytes past sp + ; - two more to absorb up to 256 blocks worth of read 254 bytes/use 256 bytes + tya + pha + jsr tc_getblock + pla + tay +.endif +.if USE_UNINTENDED_OPCODES + lax(sp),y +.else + lda(sp),y +.endif + dey + bpl init_loop + pha + +.if MEM_DECOMP_TO_API +storedadrl = * + 1 +storedadrh = * + 1 + lda #0 + cmp #OPC_STA_ZP + beq :+ + lda dp; override destination address + bne *+4 + dec dp+1 + dec dp + jmp :++ +: lda sp-2; destination address as stored in header + sta dp + lda sp-1 + sta dp+1 +: +.endif + +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc dp + sta dp + lda loadaddroffshi + adc dp+1 + sta dp+1 +.endif + + lda#$02 + bne update_sp + +literal_run: +literal_loop: + iny + lda(sp),y + sta(dp),y + dex + bmi literal_loop + + tya + pha + clc +increase_dp_by_a_and_sp_by_tos_plus_one: + adc dp + sta dp + bcc :+ + inc dp+1 +: + pla +update_sp: + sec + adc sp + sta sp + bcc :+ + inc sp+1 +.if TC_BLOCK_INTERFACE + jsr tc_getblock +.endif +: +next_command: + POLLBLOCK + + ldy#0 +.if USE_UNINTENDED_OPCODES + lax(sp),y +.else + lda(sp),y + tax +.endif + beq decrunch_done + ; literal: x = 128+length-1 + ; near copy: a = %11xxxxxx + ; far copy: a|0xf8 = >(~(offset-1)), x = 8*(length-2) | (some low bits) + asl + bcc far_copy + bpl literal_run + +near_copy: + ldx#$07 ; clear high byte of -ve offset. Also ensures copy_loop doesn't loop. + .byt $f0 ; beq (not taken) to skip over the iny +far_copy: + iny + ; carry is set for near_copy, clear for far_copy + + lda(sp),y ;fetch second byte (or for near copy, refetch first). This is low 8 bits of offset. + adc dp + sta cs + txa + ora#$f8 + adc dp+1 + sta cs+1 +.if USE_UNINTENDED_OPCODES = 0 + txa + lsr + lsr + lsr + tax +.endif + tya + pha ; save opcode length to stack + ldy#1 + lda(cs),y + sta(dp),y + +copy_loop: + iny + lda(cs),y + sta(dp),y +.if USE_UNINTENDED_OPCODES + txa ; spend an extra 2 cycles per byte here to save 10 in the bitfield extraction. A win on average + sbx#8 + bpl copy_loop +.else + dex + bpl copy_loop + clc +.endif + tya + bcc increase_dp_by_a_and_sp_by_tos_plus_one ; always taken. + +decrunch_done: + pla + iny + sta(dp),y + rts + +.if TC_BLOCK_INTERFACE +tc_getblock: + GETBLOCK sp+1 + rts +.endif + +edecrunch: diff --git a/loader/src/decompress/tsdecomp.s b/loader/src/decompress/tsdecomp.s new file mode 100644 index 0000000..39161f4 --- /dev/null +++ b/loader/src/decompress/tsdecomp.s @@ -0,0 +1,392 @@ + +decompsrc = tsget + +decompress = tsdecrunch + +.feature c_comments, leading_dot_in_identifiers + +.define .label + +/* + +decrunch_extreme.asm + +NMOS 6502 decompressor for data stored in TSCrunch format. + +Copyright Antonio Savona 2022. + +*/ + + +.define INPLACE 1 ; Enables inplace decrunching. Use -i switch when crunching. + +.label tsget = DECOMPVARS + 0 ; 2 bytes +.label tstemp = DECOMPVARS + 2 +.label tsput = decdestlo ; 2 bytes +.label lzput = DECOMPVARS + 3 ; 2 bytes + + +.if INPLACE + +.macro TS_DECRUNCH src + lda #src + sta.zp tsget + 1 + jsr tsdecrunch +.endmacro + +.else + +.macro TS_DECRUNCH(src,dst) +{ + lda #src + sta.zp tsget + 1 + lda #dst + sta.zp tsput + 1 + jsr tsdecrunch +} + +.endif + + +tsdecrunch: + jsr getfirstblock + decrunch: + + .if INPLACE + ;ldy #0 + dey + : iny + sta optRun + 1 +.if PREFER_SPEED_OVER_SIZE + ldx #$d0 ; bne opcode + and #1 + bne skp + ldx #$29 ; and immediate opcode + skp: + stx optOdd +.endif +.if MEM_DECOMP_TO_API +storedadrl = * + 1 +storedadrh = * + 1 + lda #0 + cmp #OPC_LDA_ZP + lda (tsget),y + bcs :+ + sta tsput , y ; last iteration trashes lzput, with no effect. +: + cpy #3 + bne :-- +.else + lda (tsget),y + sta tsput , y ; last iteration trashes lzput, with no effect. + cpy #3 + bne :- +.endif + pha +.if LOADCOMPD_TO + clc + lda loadaddroffslo + adc tsput + sta tsput + lda loadaddroffshi + adc tsput + 1 + sta tsput + 1 + sec +.endif + tya + ldy #0 + beq update_getonly + .else + ldy #0 + + lda (tsget),y + sta optRun + 1 + + ldx #$d0 //bne opcode + and #1 + bne !skp+ + ldx #$29 //and immediate opcode + !skp: + stx optOdd + + inc tsget + bne entry2 + inc tsget + 1 + .endif + + entry2: + POLLBLOCK + + lax (tsget),y + + bmi rleorlz + + cmp #$20 + bcs lz2 + ; literal + + .if INPLACE + + inc tsget + beq updatelit_hi + return_from_updatelit: + and #1 + bne odd_lit + + ts_delit_loop: + + lda (tsget),y + sta (tsput),y + iny + dex + odd_lit: + lda (tsget),y + sta (tsput),y + iny + dex + + bne ts_delit_loop + + tya + tax + ; carry is clear + ldy #0 + .else ; not inplace + tay + + and #1 + bne !odd+ + + ts_delit_loop: + + lda (tsget),y + dey + sta (tsput),y + !odd: + lda (tsget),y + dey + sta (tsput),y + + bne ts_delit_loop + + txa + inx + .endif + + updatezp_noclc: + adc tsput + sta tsput + bcs updateput_hi + putnoof: + txa + update_getonly: + adc tsget + sta tsget + bcc entry2 + jsr getblock + bcc entry2 + + .if INPLACE + updatelit_hi: + pha + jsr getblock + pla + tax + bcc return_from_updatelit + .endif + updateput_hi: + inc tsput+1 + clc + bcc putnoof + + ; LZ2 + lz2: + beq done + + ora #$80 + adc tsput + sta lzput + lda tsput + 1 + sbc #$00 + sta lzput + 1 + + ; y already zero + lda (lzput),y + sta (tsput),y + iny + lda (lzput),y + sta (tsput),y +.if PREFER_SPEED_OVER_SIZE + tya + dey + + adc tsput + sta tsput + bcs lz2_put_hi + skp_lz2: + inc tsget + bne entry2 + jsr getblock + bcc entry2 + + lz2_put_hi: + inc tsput + 1 + bcs skp_lz2 +.else + tya ; y = a = 1. + tax ; y = a = x = 1. a + carry = 2 + dey ; ldy #0 + + beq updatezp_noclc +.endif + rleorlz: + + alr #$7f + bcc ts_delz + + ; RLE + beq zeroRun + + plain: + + ldx #2 + iny + sta tstemp ; number of bytes to de-rle + + lsr ; c = test parity + + lda (tsget),y ; fetch rle byte + ldy tstemp + runStart: + sta (tsput),y + + bcs odd_rle + sec + + ts_derle_loop: + dey + sta (tsput),y + odd_rle: + + dey + sta (tsput),y + + bne ts_derle_loop + + ; update zero page with a = runlen, x = 2 , y = 0 + lda tstemp + bcs updatezp_noclc + + + done: +.if INPLACE + pla + sta (tsput),y +.endif + rts + + + ; LZ + ts_delz: + + lsr + sta lzto + 1 + + iny + + lda tsput + bcc long + + sbc (tsget),y + sta lzput + lda tsput+1 + + sbc #$00 + + ldx #2 + ; lz MUST decrunch forward + lz_put: + sta lzput+1 + + ldy #0 + + lda lzto + 1 + lsr + bcs odd_lz + + lda (lzput),y + sta (tsput),y + ts_delz_loop: + iny + + odd_lz: + + lda (lzput),y + sta (tsput),y + + iny + + lda (lzput),y + sta (tsput),y + + lzto: cpy #0 + bne ts_delz_loop + + tya + + ; update zero page with a = runlen, x = 2, y = 0 + ldy #0 + ; clc not needed as we have len - 1 in A (from the encoder) and C = 1 + jmp updatezp_noclc + + zeroRun: + optRun: ldy #255 +.if PREFER_SPEED_OVER_SIZE + sta (tsput),y + optOdd: bne odd_zero + ts_dezero_loop: + dey + sta (tsput),y + odd_zero: + dey + sta (tsput),y + bne ts_dezero_loop + + lda optRun + 1 + + ldx #1 + jmp updatezp_noclc +.else + sty tstemp + tya + alr #$01 + ldx #1 + bne runStart +.endif + long: + ; carry is clear and compensated for from the encoder + adc (tsget),y + sta lzput + iny + lax (tsget),y + ora #$80 + adc tsput + 1 + + cpx #$80 + rol lzto + 1 + ldx #3 + + bne lz_put + +getblock: + inc tsget + 1 +getfirstblock: + GETBLOCK tsget + 1 + clc + rts diff --git a/loader/src/decompress/zx0decomp.s b/loader/src/decompress/zx0decomp.s new file mode 100644 index 0000000..424de30 --- /dev/null +++ b/loader/src/decompress/zx0decomp.s @@ -0,0 +1,672 @@ + +; +; (c) Copyright 2021 by Tobias Bindhammer. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * The name of its author may not be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; + +.FEATURE labels_without_colons, leading_dot_in_identifiers + +.if MEM_DECOMP_TO_API + ; cannot perform src == dst check when not depacking in-place, + ; as offsets (arbitrary src - original src) or (arbitrary dst - original dst) are not known, + ; also cannot copy remaining uncompressed blob of unknown size + .error "***** MEM_DECOMP_TO_API is not supported for ZX0. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****" +.endif + +.if PREFER_SPEED_OVER_SIZE + +; faster but bigger decruncher + +decompsrc = lz_src + +lz_bits = BLOCKINDEX + 1 +lz_src = DECOMPVARS + 0 +lz_dst = decdestlo +lz_len_hi = DECOMPVARS + 4 + +; USE_DALI is defined in resident.s +.if USE_DALI +.define set_lz_bit_marker ror +.define get_lz_bit lsr 0? dop or bcs to disable and enable, 80 or b0 + lda A = 1 with rol, but not if we copy literal this way + get_lz_bit + bcs .lz_match ;either match with new offset or old offset + + ;------------------ + ;REPEAT LAST OFFSET + ;------------------ +.lz_repeat + get_lz_bit + bcs :++ +: + get_lz_bit ;fetch payload bit + rol ;can also moved to front and executed once on start + get_lz_bit ;cheaper with 2 branches, as initial branch to .lz_literal therefore is removed + bcc :- +: + bne :+ + jsr .lz_refill_bits ;fetch more bits + beq .lz_m_page ;XXX TODO sec after sbc #1 is also sufficient, but slower +: + sbc #$01 ;subtract 1, will be added again on adc as C = 1 +.lz_match_big ;we enter with length - 1 here from normal match + eor #$ff + tay +.lz_m_page + eor #$ff ;restore A + + adc first bit for lenth is in carry, and we have %0xxxxxxx xxxxxxxx as offset + sta .lz_offset_lo + 1 ;lobyte of offset + + inc A = 1 + shf <.lz_bits + ;rol + ;bne .lz_match + ;else A = 0 + ;but only for lowbyte?! + bcs .lz_match ;either match with new offset or old offset + + ;------------------ + ;DO MATCH + ;------------------ +.lz_repeat + jsr .lz_length + sbc #$01 + bcc .lz_dcp ;fix highbyte of length in case and set carry again (a = $ff -> compare delivers carry = 1) + ;sec ;XXX TODO in fact we could save on the sbc #$01 as the sec and adc later on corrects that again, but y would turn out one too less +.lz_match_big ;we enter with length - 1 here from normal match + eor #$ff + tay + ;XXX TODO save on eor #$ff and do sbc lz_dst + 0? + eor #$ff ;restore A +.lz_match_len2 ;entry from new_offset handling + adc <.lz_dst + 0 + sta <.lz_dst + 0 + tax ;remember for later end check, cheaper this way + bcs .lz_clc ;/!\ branch happens very seldom, if so, clear carry + dec <.lz_dst + 1 ;subtract one more in this case +.lz_clc_back +.if OFFSET_OPT +.lz_offset_lo sbc #$00 ;carry is cleared, subtract (offset + 1) in fact we could use sbx here, but would not respect carry, but a and x are same, but need x later anyway for other purpose +.else + sbc <.lz_offset +.endif + sta .lz_msrcr + 0 + lda <.lz_dst + 1 +.if OFFSET_OPT +.lz_offset_hi sbc #$00 +.else + sbc <.lz_offset + 1 +.endif + sta .lz_msrcr + 1 + ; ;XXX TODO would have dst + 0 and + 1 in X and A here, of any use? +.lz_cp_match + ;XXX TODO if repeated offset: add literal size to .lz_msrcr and done? +.lz_msrcr = * + 1 + lda $beef,y + sta (.lz_dst),y + iny + bne .lz_cp_match + inc <.lz_dst + 1 + + lda <.lz_len_hi ;check for more loop runs + bne .lz_m_page ;do more page runs? Yes? Fall through +.lz_check_poll + cpx <.lz_src + 0 ;check for end condition when depacking inplace, .lz_dst + 0 still in X +.lz_skip_poll bne .lz_start_over ;-> can be changed to .lz_poll, depending on decomp/loadcomp + + lda <.lz_dst + 1 + sbc <.lz_src + 1 + bne .lz_start_over + + ;jmp .ld_load_raw ;but should be able to skip fetch, so does not work this way + ;top ;if lz_src + 1 gets incremented, the barrier check hits in even later, so at least one block is loaded, if it was $ff, we at least load the last block @ $ffxx, it must be the last block being loaded anyway + ;as last block is forced, we would always wait for last block to be loaded if we enter this loop, no matter how :-) + + ;------------------ + ;NEXT PAGE IN STREAM + ;------------------ +.lz_init + bne :+ +.lz_next_page + inc <.lz_src + 1 +: +.if LOAD_TO_RAM_UNDER_IO + jmp getblock +.else + php + txa + pha + GETBLOCK <.lz_src + 1 + pla + tax + plp + rts +.endif + ;------------------ + ;FETCH A NEW OFFSET + ;------------------ +: ;lz_length as inline + shf <.lz_bits ;fetch payload bit + rol ;can also moved to front and executed once on start +.lz_match + shf <.lz_bits + bcc :- + + bne :+ + jsr .lz_refill_bits +: + sbc #$01 ;XXX TODO can be omitted if just endposition is checked, but 0 does not exist as value? + bcc .lz_eof ;underflow. must have been 0 + + lsr +.if OFFSET_OPT + sta .lz_offset_hi + 1 ;hibyte of offset +.else + sta .lz_offset + 1 ;hibyte of offset +.endif + lda (.lz_src),y ;fetch another byte directly + ror +.if OFFSET_OPT + sta .lz_offset_lo + 1 +.else + sta .lz_offset +.endif + inc <.lz_src + 0 ;postponed, so no need to save A on next_page call + bne :+ + jsr .lz_next_page ;preserves carry, all sane +: + lda #$01 + ldy #$fe + bcs .lz_match_len2 ;length = 1 ^ $ff, do it the very short way :-) +: + shf <.lz_bits ;fetch first payload bit + ;XXX TODO we could check bit 7 before further shf? + rol ;can also moved to front and executed once on start + shf <.lz_bits + bcc :- + bne .lz_match_big + ldy #$00 ;only now y = 0 is needed + jsr .lz_refill_bits ;fetch remaining bits +; bcs .lz_match_big + jmp .lz_match_big + + ;------------------ + ;SELDOM STUFF + ;------------------ +.lz_clc + clc + bcc .lz_clc_back +.lz_m_page + dec <.lz_len_hi + inc .lz_msrcr + 1 ;XXX TODO only needed if more pages follow + bne .lz_cp_match + + ;------------------ + ;ELIAS FETCH + ;------------------ +.lz_refill_bits + tax + lda (.lz_src),y + rot + sta <.lz_bits + inc <.lz_src + 0 ;postponed, so no need to save A on next_page call + bne :+ ;XXX TODO if we would prefer beq, 0,2% saving + jsr .lz_next_page ;preserves carry and A, clears X, Y, all sane +: + txa + bcs .lz_lend + + ;lda #$00 + ;slo <.lz_bits +.lz_get_loop + shf <.lz_bits ;fetch payload bit +.lz_length_16_ + rol ;can also moved to front and executed once on start + bcs .lz_length_16 ;first 1 drops out from lowbyte, need to extend to 16 bit, unfortunatedly this does not work with inverted numbers +.lz_length + shf <.lz_bits + + bcc .lz_get_loop + beq .lz_refill_bits +.lz_lend +.lz_eof + rts +.lz_length_16 ;happens very rarely + pha ;save LSB + tya ;was lda #$01, but A = 0 + rol makes this also start with MSB = 1 + jsr .lz_length_16_ ;get up to 7 more bits + sta <.lz_len_hi ;save MSB + pla ;restore LSB + rts + +.if LOAD_TO_RAM_UNDER_IO +getblock php + txa + pha + GETBLOCK <.lz_src + 1 + pla + tax + plp + rts +.endif + +.endif diff --git a/loader/src/drives/drivecode-common.inc b/loader/src/drives/drivecode-common.inc new file mode 100644 index 0000000..209cb77 --- /dev/null +++ b/loader/src/drives/drivecode-common.inc @@ -0,0 +1,527 @@ + +.ifndef _DRIVECODE_COMMON_INC_ +_DRIVECODE_COMMON_INC_ = 1 + +MIN_DEVICE_NO = 8 ; these two settings define the device number range when scanning for devices in install.s, +MAX_DEVICE_NO = 30 ; devices beyond this range will not be recognized or useable + +; 1541/41-C/41-II/41U/70/71/71CR +MINSTEPSPEED = $18 ; min. R/W head stepping speed +MAXSTEPSPEED = $10 ; max. R/W head stepping speed +STEPPERACC = $0c ; R/W head stepping acceleration, smaller is slower acceleration +SINGLESTEPSPEED = $0c ; time between two consecutive half-track steps for a single-track step + +.macro ENABLE_WATCHDOG + .if ::DISABLE_WATCHDOG + sei + .else + cli + .endif +.endmacro + +.macro DRIVEGETBYTE drivetype, comparelbl, nozeroes + .if .not .xmatch (drivetype, 1581) + .local OFFSET + .local getbit + + ; must not clobber x + + .if .paramcount < 3 + OFFSET = 0 + + ldy VIA1_PRB + OFFSET + .else + OFFSET = 16 + .endif + + lda #%10000000 +getbit: cpy VIA1_PRB + OFFSET + beq getbit + ldy VIA1_PRB + OFFSET +comparelbl: cpy #ATN_IN | ATNA_OUT | CLK_IN + ror + bcc getbit + + .assert .hibyte(*) = .hibyte(getbit), error, "***** Page boundary crossing in getbyte loop, fatal cycle loss. *****" + .else + .local getbit + .local waitbit + + lda #%10000000 +getbit: pha + lda CIA_PRB +waitbit: cmp CIA_PRB + beq waitbit + lda CIA_PRB + and #CLK_IN + cmp #CLK_IN + pla + ror + bcc getbit + + .assert .hibyte(*) = .hibyte(getbit), error, "***** Page boundary crossing in getbyte loop, fatal cycle loss. *****" + .endif +.endmacro + +.macro DRIVESENDBYTE drivetype, sendvalue + .local sendbit + .local waitbit + + sta sendvalue + ldy #8 +sendbit: asl sendvalue + lda #0 + .if .not .xmatch (drivetype, 1581) + adc #ATNA_OUT | DATA_IN + sta VIA1_PRB + lda VIA1_PRB +waitbit: cmp VIA1_PRB + .else + adc #DATA_IN + sta CIA_PRB + lda CIA_PRB +waitbit: cmp CIA_PRB + .endif + beq waitbit + dey + bne sendbit +.endmacro + +.macro GET_FILENAME drivetype + .if .not .xmatch (drivetype, 1581) + ;ldx #0 + .if .xmatch (drivetype, 1571) + lda #ATNA_OUT; ATNA_OUT set, CLK_OUT and DATA_OUT clear: drive is ready + sta (V1B,x); VIA1_PRB + .endif +getfilenam: + .if .xmatch (drivetype, 1541) + inx + getbytewdog41 = getbytewdg + getbyterts41 = getbyterts +getbytewdg: lda #$f5 + sta VIA1_PRB; ATNA_OUT set, CLK_OUT and DATA_OUT clear: drive is ready + sta TIMER; reset watchdog time-out + ENABLE_WATCHDOG + getbyte41 = getbyte +getbyte: DRIVEGETBYTE 1541, getbytecmp41 +getbyterts: sta a:FILENAME - 1,x + .else + inx + jsr getbytewdg + sta .lobyte(FILENAME - 1),x + .endif + .else; 1581 + ldx #0 + stx CIA_PRB; CLK_OUT and DATA_OUT clear: drive is ready +getfilenam: inx + jsr getbyte + sta filename - 1,x + .endif + bne getfilenam + + sei; disable watchdog + cpx #16 + 3 + .if .xmatch (drivetype, 1541) + bcs getcustom + .else + bcc :+ + jmp getcustom +: + .endif + .if .not .xmatch (drivetype, 1581) + .if .xmatch (drivetype, 1541) + dec VIA1_PRB; %1xy10000 -> %1xy01111: drive busy, set CLK_OUT and DATA_OUT + .else + lda #ERR | ATNA_OUT | CLK_OUT | DATA_OUT; drive busy + sta VIA1_PRB + sta ERRORCOUNT; reset, as spinning up the motor would yield errors + .endif + .else + lda #CLK_OUT | DATA_OUT; drive busy + sta CIA_PRB + .endif + .if .xmatch (drivetype, 1571) + inc prpnxtfjmp; OPC_JMP_ABS -> OPC_EOR_ABS, disable block caching: do not jump to prpdnxtfil (idleloop) after fetching the file's first block + .endif +: dex ; if zero-length filename, + beq loadnextfile ; use hash values of next file + cpx #FILENAME_MAXLENGTH + 1 + bcs :- +findfile: sec + jsr gethashval + .if .not .xmatch (drivetype, 1581) +setnextfile: + .endif + sta FILENAMEHASHLO + sty FILENAMEHASHHI +loadnextfile: +.endmacro; GET_FILENAME drivetype + +; matches against hash of filename in FILENAMEHASHLO/HI + +.macro FIND_FILE drivetype + .if .not .xmatch (drivetype, 1581) +chknewdisk: + .if .xmatch (drivetype, 1541) + lda #$fc + sta CYCLESTARTENDSECTOR + ldx NUMFILES + bpl findloop + + ; a new disk has been inserted + sta DIRBLOCKPOS + jsr getblkbam; store ID, sector link sanity check + bcc chknewdisk + jsr checkchg + lda #1; first dir sector + .else + lda #0; BAM sector + sta CYCLESTARTENDSECTOR + ldx NUMFILES + bpl findloop + + ; a new disk has been inserted + ;lda #0; BAM sector + sta DIRBLOCKPOS + + ldx #.lobyte(storeid - (idswitch + 2)) + jsr getblkstid; store ID, sector link sanity check + bcs chknewdisk + jsr checkchg + lda #1; first dir sector + + ; set seek boundaries according to number of disk sides + ldx #MAXTRACK71 + 1 + ldy #NUMTRACKS_A + bit BLOCKBUFFER71 + TWOSIDEDOFFSET + bmi :+ + ldx #NUMTRACKS_SINGLESIDED + 1 + ldy #NUMTRACKS_SINGLESIDED +: stx MAXTRACK + sty MAXTRACK_A + .endif + + ;lda LINKSECTOR + .else; 1581 + inc prpnxtfjmp; OPC_JMP_ABS -> OPC_EOR_ABS, do not jump to after prepare-next-file routine + +prpnxtfile: lda NUMFILES + bmi newdisk +diskchangd: lda #$00 + bne newdisk + jmp samedisk + +newdisk: ; a new disk has been inserted + jsr getdirtrk + ldy #$00 + sty diskchangd + 1 + sty CYCLESTARTENDTRACK + sty CYCLESTARTENDSECTOR + jsr getblock81 + bcs newdisk + + ;lda LINKTRACK + ;ldx LOADEDSECTOR; $00 + ;ldy LINKSECTOR + sty FIRSTDIRSECTOR + .endif; 1581 + + ; directory cycling: fill the directory buffer with the next file entries, + ; this is also executed upon file not found in the currently buffered directory segment + ; (with accu containing NEXTDIRBLOCKSECTOR's value) +nextdirseg: + .if .not .xmatch (drivetype, 1581) + ldy #$ff + .else + ldx #$ff + .endif + ; file not found (yet) + .if .xmatch (drivetype, 1541) + ldx CYCLESTARTENDSECTOR + beq filenotfnd; branch if cycle complete with z = 0 and y = $ff = diskio::status::FILE_NOT_FOUND + .else; !1541 + bit CYCLESTARTENDSECTOR + bpl :+ ; branch if cycle not complete + sec + jmp filenotfnd +: + .endif; !1541 + .if .not .xmatch (drivetype, 1581) + sty NUMFILES + + .if .xmatch (drivetype, 1541) +nextdirsct: ; a = sector + jsr getblkdir; compare ID, sector link sanity check + bcc chknewdisk + ;lda LINKSECTOR41 + ;ldx LOADEDSECTOR + tay + bpl :+; branch if not wrapping to first dir block + lda #1; wrap around to first dir sector +: sta NEXTDIRBLOCKSECTOR + .else; 1571 + SKIPWORD; do not set CYCLESTARTENDSECTOR +dircycle: sta CYCLESTARTENDSECTOR +nextdirsct: ; a = sector + jsr getblkchid; compare ID, sector link sanity check + bcs chknewdisk + + ;lda LINKSECTOR + ;ldy LINKTRACK + ;cpy CURTRACK + beq :+; branch if not wrapping to first dir block + lda #1; wrap around to first dir sector +: sta NEXTDIRBLOCKSECTOR + .endif + .else; 1581 + stx NUMFILES + +nextdirsct: ; a = track + ; y = sector + sta CURRDIRBLOCKTRACK + jsr getblock81 + bcs newdisk; start over on error + ;ldy LINKSECTOR + ;lda LINKTRACK + bne :+; branch if not wrapping to first dir block + jsr getdirtrk + ldy FIRSTDIRSECTOR +: sta NEXTDIRBLOCKTRACK + sty NEXTDIRBLOCKSECTOR + + ldy #0 + .endif; 1581 + +getdirloop: + .if .xmatch (drivetype, 1541) + ldy DIRBLOCKPOS + jsr sertorawr; get file's start track + beq notafile; skip non-files denoted by track 0 + cmp #MAXTRACK41 + 1 + bcs notafile; or files with invalid start tracks + inc NUMFILES + ldx NUMFILES + sta DIRTRACKS,x + jsr sertorawd; get file's start sector + sta DIRSECTORS,x + lda (BLP),y; number of file blocks (low-byte) + jsr sertoraw41 + sta NUMBLOCKS,x + .elseif (.xmatch (drivetype, 1571)) + ldy DIRBLOCKPOS + lda BLOCKBUFFER + 3 + TRACKOFFSET,y; get file's start track + beq notafile; skip non-files denoted by track 0 + cmp MAXTRACK + bcs notafile; or files with invalid start tracks + inc NUMFILES + ldx NUMFILES + sta .lobyte(DIRTRACKS),x + lda BLOCKBUFFER + 3 + SECTOROFFSET,y; get file's start sector + sta .lobyte(DIRSECTORS),x + lda BLOCKBUFFER + BLOCKSOFFSET,y + sta .lobyte(NUMBLOCKS),x + .else; 1581 + lda BLOCKBUFFER + 3 + TRACKOFFSET,y; get file's start track + beq notafile; skip non-files denoted by track 0 + inc NUMFILES + ldx NUMFILES + sta DIRTRACKS,x + lda BLOCKBUFFER + 3 + SECTOROFFSET,y; get file's start sector + sta DIRSECTORS,x + tya + pha + .endif; 1581 + ldx #0 + jsr fnamehash + ldx NUMFILES; restore x + sta FILENAMEHASHVALSLO,x + .if .not .xmatch (drivetype, 1581) + sty FILENAMEHASHVALSHI,x + .else + tya + sta FILENAMEHASHVALSHI,x + pla + tay + .endif + ; advance to next file or quit loop + cpx #DIRBUFFSIZE - 1 + bcs dirbuffull +notafile: + .if .xmatch (drivetype, 1541) + lax DIRBLOCKPOS + axs #$20; 8 entries per block, $20 bytes per entry + stx DIRBLOCKPOS + bcs getdirloop; process all entries in a dir block + .elseif .xmatch (drivetype, 1571) + lax DIRBLOCKPOS + axs #.lobyte(-$20); 8 entries per block, $20 bytes per entry + stx DIRBLOCKPOS + bcc getdirloop; process all entries in a dir block + .else; 1581 + tya + and #%11100000; 8 entries per block, $20 bytes per entry + ;clc + adc #$20 + tay + bcc getdirloop; process all entries in a dir block + .endif + ; process next dir block + + .if .xmatch (drivetype, 1541) + lax NEXTDIRBLOCKSECTOR + tay + eor CYCLESTARTENDSECTOR + bmi :+; set CYCLESTARTENDSECTOR + bne :++ + ; cycle complete + tay +: sty CYCLESTARTENDSECTOR +: txa + bpl nextdirsct; jmp + .elseif .xmatch (drivetype, 1571) + lda NEXTDIRBLOCKSECTOR + ldy CYCLESTARTENDSECTOR + beq dircycle; set CYCLESTARTENDSECTOR + cmp CYCLESTARTENDSECTOR + bne nextdirsct + + ; cycle complete + ;sec + ror CYCLESTARTENDSECTOR + .if (DIRBUFFSIZE < 16) + ; always fill up a dir buffer that can hold fewer than 2 dir blocks, because if the dir is 2 blocks + ; big, the dir buffer may otherwise only hold parts of 2 blocks despite having more space + bne nextdirsct; jmp + .endif + .else; 1581 + jsr getdirtrk + ldy NEXTDIRBLOCKSECTOR + ldx CYCLESTARTENDTRACK + bne :+ + sta CYCLESTARTENDTRACK + sty CYCLESTARTENDSECTOR + beq nextdirsct; jmp +: cmp CYCLESTARTENDTRACK + bne nextdirsct + cpy CYCLESTARTENDSECTOR + bne nextdirsct + + ; cycle complete + ;sec + ror CYCLESTARTENDSECTOR + .endif; 1581 +dirbuffull: lda LOADEDSECTOR + sta NEXTDIRBLOCKSECTOR + .if .not .xmatch (drivetype, 1581) + .if DIRBUFFSIZE < 16 + ;ldx NUMFILES; actually number of files - 1 + .else + ldx NUMFILES; actually number of files - 1 + .endif + .else + lda CURRDIRBLOCKTRACK + sta NEXTDIRBLOCKTRACK + + ; the disk was not changed, or the dir has just been read +samedisk: ldx NUMFILES; actually number of files - 1 + .endif; 1581 + +findloop: + .if .not .xmatch (drivetype, 1581) + lda NEXTDIRBLOCKSECTOR + .else; !1581 + lda NEXTDIRBLOCKTRACK + ldy NEXTDIRBLOCKSECTOR + .endif + dex ; skip the last file entry to keep it as an overflow entry for load-next and PREPARE_NEXT_FILE + .if .xmatch (drivetype, 1541) + stx FILEDIRBUFFERINDEX + .endif + bmi nextdirseg; if the dir buffer does not contain the file, cycle through the directory to find it + +nextfile: lda FILENAMEHASHVALSLO,x + eor FILENAMEHASHLO + bne findloop + ldy FILENAMEHASHVALSHI,x + cpy FILENAMEHASHHI + bne findloop + + ; file found + + .if .xmatch (drivetype, 1541) + ;sec + .elseif (.xmatch (drivetype, 1571)) + ; check for illegal track or sector + ;sec + stx FILEDIRBUFFERINDEX + lda .lobyte(DIRTRACKS),x + jsr getnumscts + stx TEMP + ldx FILEDIRBUFFERINDEX + ldy .lobyte(DIRTRACKS),x + lda .lobyte(DIRSECTORS),x + cmp TEMP + ;bcs filenfound +filenotfnd: + .else; 1581 + ;lda #$00 + sta CYCLESTARTENDTRACK + sta CYCLESTARTENDSECTOR + + lda DIRTRACKS,x + ldy DIRSECTORS,x + clc +filenotfnd: +prpnxtfjmp: jmp prpdnxtfil; is changed to eor prpdnxtfil + dec prpnxtfjmp; OPC_EOR_ABS -> OPC_JMP_ABS + .endif; 1581 +.endmacro; FIND_FILE drivetype + +.macro FNAMEHASH drivetype + +fnamehash: + .if .xmatch (drivetype, 1541) + jsr sertorawd + .else + lda BLOCKBUFFER + 5,y + iny + .endif + cmp #' ' | $80; $a0 = end of filename + beq gethashval + .if .not .xmatch (drivetype, 1581) + sta .lobyte(FILENAME),x + .else; 1581 + sta filename,x + .endif; 1581 + inx + cpx #FILENAME_MAXLENGTH + bcc fnamehash + + ; fall through + +gethashval: txa + tay + ;sec +hashloop: sta TEMP + tya + .if .not .xmatch (drivetype, 1581) + adc .lobyte(FILENAME - 1),x + .else; 1581 + adc filename - 1,x + .endif; 1581 + tay + adc TEMP + dex + bne hashloop + + .if .xmatch (drivetype, 1541) +specdone: ;sec + ror SPECWRAPSECTOR +getcustom: + .endif + rts + +.endmacro; FNAMEHASH drivetype + +.endif; !_DRIVECODE_COMMON_INC_ diff --git a/loader/src/drives/drivecode1541.s b/loader/src/drives/drivecode1541.s new file mode 100755 index 0000000..ee53b9c --- /dev/null +++ b/loader/src/drives/drivecode1541.s @@ -0,0 +1,1336 @@ + +.include "loader.inc" + +.include "cpu.inc" +.include "via.inc" + +.include "drives/drivecode-common.inc" + +HEADERTRACK = $18; fixed: OPC_CLC, HEADERTRACK = BLOCKINDEX_STATUS = SPECSTARTSECTOR +BLOCKINDEX_STATUS = HEADERTRACK +SPECSTARTSECTOR = HEADERTRACK +NUMSECTORSONTRACK = $19; fixed +STACKBUFFER = $20; 4 bytes +PREVBLOCKINDEXPLUS1 = $29; fixed: BITRATECOUNT +SPECULATIVEINTERLEAVE = $38; fixed: OPC_SEC +MAXCONFIRMEDBLKIDXSEC = $39; fixed: SUCCESSCOUNT +CURRBLOCKINDEX = $54 +ERRORCOUNT = $58; fixed +DISKCHANGEBUFFER = $56 +ID041 = $59 +ID1 = $5a; must be ID041 + 1 +LOADEDSECTOR = $5c +BLOCKINDEXBASE = $5e +NEXTDIRBLOCKSECTOR = $66 +SPECWRAPSECTOR = $6c; SPECWRAPSECTOR = CLEARINDEXMASK +CLEARINDEXMASK = SPECWRAPSECTOR +SPECWRAPSECTORINDEX = $6e; SPECWRAPSECTORINDEX = CLEARIDXMIN +CLEARIDXMIN = SPECWRAPSECTORINDEX +FILENAMEHASHLO = $70 +FILENAMEHASHHI = $71 +MAXCONFIRMEDBLOCKINDEX = $72 +NUMCONTIGUOUSBLOCKS = $74 +FILESECTOR41 = $76; FILESECTOR41 = NEXTCONTIGUOUSBLOCK = CYCLESTARTENDSECTOR +NEXTCONTIGUOUSBLOCK = FILESECTOR41 +CYCLESTARTENDSECTOR = FILESECTOR41 +NUMFILES = $78; fixed: OPC_SEI +CURRTRACK41 = $7a; fixed: OPC_NOP + +SECTORLINKTABLE41 = $3e; INDEXTABLE + MAXNUMSECTORS; length is MAXNUMSECTORS = $15 bytes + +DIRBLOCKPOS = $e0 +FILEDIRBUFFERINDEX = $e1 +FILENAMEHASHVALSHI = $e2; length is DIRBUFFSIZE + +LEDSTATE41 = FILENAMEHASHVALSHI + DIRBUFFSIZE; must be INDEXTABLE - 2: see trackinit +REQUESTEDSECTOR41 = LEDSTATE41 + 1; = FILETRACK, must be INDEXTABLE - 1: see trackinit +FILETRACK = REQUESTEDSECTOR41 +INDEXTABLE = REQUESTEDSECTOR41 + 1; length is MAXNUMSECTORS = $15 bytes +FILENAME41 = INDEXTABLE; max. 16 bytes + +DIRBUFFSIZE = 11 + +LINKTRACK41 = .lobyte(decode2 + 1) +LINKSECTOR41 = .lobyte(decode6 + 1); LINKSECTOR41 = TEMP = BLOCKSIZE +TEMP = LINKSECTOR41 +BLOCKSIZE = TEMP +GENSPECLIMIT = .lobyte(decode1 + 1) +CURRSTEPSPEEDLOW = .lobyte(decode4 + 1) + +BLOCKBUFFER41 = $0100 + +DECODETABLE = $0700; effectively starts at $0722 + +DIRTRACKS = $0703 +DIRSECTORS = DIRTRACKS + DIRBUFFSIZE + +ROMOS_TRACK_DIFF = $42 + +MAXINTERLEAVE = 16 +MAXNUMSECTORS = 21 +MAXTRACK41 = 41 + +TIMER = VIA2_T1C_H +TIMERLO = VIA2_T1C_L +TIMER2 = VIA1_T1C_H +TIMER2LO = VIA1_T1C_L + +INDEXSPECULATIVE = %01000000 +BLOCKPENDING = %00100000 + + .assert DIRBUFFSIZE >= 9, error, "***** Dir buffer too small. *****" + +NMS = MAXNUMSECTORS; NUMSECTORSONTRACK initial value +NFL = $ff ; NUMFILES initial value +SIL = 4 ; SPECULATIVEINTERLEAVE initial value + +V1 = VIA1_PRB +V1B = $07 +V1B41 = V1B +V2 = VIA2_PRB +V2B = $0f +V2B41 = V2B + +_E0 = $0b; fixed +EF = $2e; fixed, ~ATNA_OUT +FE = $30; fixed + +T1 = TIMER +TI1 = $11 +T2 = TIMER2 +TI2 = $27 + +BL = STACK - $1a; offset for number of file blocks in directory sector +BLP = $68; fixed +ST = STACK +STK = $69; fixed + +CLEARSECTORLINKTABLE = STK; fixed +SF = STACK + $fe +SFE = FE; fixed + +ERR = $80; ERRORCOUNT initial value, $40 is too little: would cause stepping when spinning up +ERT = $20; ERRORCOUNT retry value + +BTR = 4; number of read attempts before bit-rate cycling on initial current track retrieval +SCN = 10; number of consecutive successful per-bit-rate attempts on initial current track retrieval +BITRATECOUNT = PREVBLOCKINDEXPLUS1 +SUCCESSCOUNT = MAXCONFIRMEDBLKIDXSEC + +___ = REPOSITORY_VERSION; will be initialised dynamically + + .org 3 + +drvcodebeg41: + .byte .hibyte(drvcodeend41 - * + $0100 - $01); init transfer count hi-byte + + ; $d0 doubles as JOBCODE_JUMP to $0700 to handle the watchdog IRQ + .byte $d0, $40, $90, $00, >V1, $20, $60, $e0, $c0, $a0, $80, $60; ZP $00..$03: end of INDEXTABLE + .byte >V2, T2, BTR, $6f, $30, $ff, $39, $ef, $31 + .byte SF, $0f, $36, $3f, $3c, $2f, $34, SIL, SCN, $4f, $32, $7f, $38 + +findtrkerr: dec BITRATECOUNT + beq :+ + jmp findtrknum + +: lax VIA2_PRB + axs #$0100 - (1 << BITRATE_SHIFT) + stx VIA2_PRB; cycle through the 4 bit-rates + lda #BTR + jmp findtrkstp + + .byte ___, $2e, ___, $2f, ___, $27, ERR, ___, ___, $2a, ___, $2b, ___, $23 + +todataread: bne readcontd ; 54 + jmp dataread ; 57 59 66 70 + + .byte $2d, ___, $25, ST, $20, ___, $29, ___, $21 + +tofndtrerr: bcc findtrkerr; jmp + + .byte ___, $26, ___, $2c, ___, $24, NFL + + ; bit-rates %11 %10 %01 %00 +readloop00: .byte OPC_NOP_ABS ; 9 ; ; nop $22xy + .byte ___, $22 +readloop01: php ; 8 12 + .byte $28; plp + ;plp ; 12 16 +readloop10: +readloop11: +_02 = * + 2; .hibyte(DECODETAB6) = 2 +decode6: adc DECODETAB6,y ; 7 9 16 20 ; --6-5566 -> $x0 ; y = --6-----, lsb = ----5566 + pha ; 10 +V2A = * + 1 + lax VIA2_PRA ; 14 16 23 27 ; 00000111 ; 0 - %11: [0..25], %10: [0..27], %01: [0..29], %00: [0..31] + eor #%11111000 ; 16 + sax .lobyte(decode1 + 1) ; 19 ; -----111 +_09 = *; OPC_ORA_IMM = $09 + ora #%00000111 ; 21 ; 00000HHH + tay ; 23 + ldx #%00111110 ; 25 + lda VIA2_PRA ; 29 31 38 42 ; 11222223 ; 1 - %11: [26..51], %10: [28..55], %01: [30..59], %00: [32..63] + sax .lobyte(decode2 + 1) ; 32 ; --22222- + alr #%11000001 ; 34 ; -11-----:3 + tax ; 36 + lda DECODETAB0 - %00000111,y ; 40 ; 00000HHH -> $x0 +decode1: eor DECODETAB1,x ; 44 ; -11--111 -> $0x, x = -11-----, lsb = -----111 + tsx ; 46 + pha ; 49 +densty11sw: beq todataread ; 51 57 64 68 ; ; %11: beq todataread - %10, %01, %00: bvs todataread +readcontd: eor STACK + 1,x ; 55 + eor STACK + 2,x ; 59 + eor STACK + 3,x ; 63 +checksum: eor #0 ; 65 +readdata: sta checksum + 1 ; 68 ; ; entry + lda VIA2_PRA ; 72 78 85 89 ; 3:33334444 ; 2 - %11: [52..77], %10: [56..83], %01: [60..89], %00: [64..95] + ldx #%00001111 ; 74 + sax .lobyte(decode4 + 1) ; 77 ; ----4444 + arr #%11110000 ; 79 ; 33333--- + tay ; 81 +decode2: lda $00 ; 84 ; --22222- -> $x0 + eor DECODETAB3,y ; 88 ; 33333--- -> $0x + pha ; 91 + lda VIA2_PRA ; 95 101 108 112 ; 45555566 ; 3 - %11: [78..103], %10: [84..111], %01: [90..119], %00: [96..127] + sax .lobyte(decode6 + 1) ; 98 ; ----5566 + alr #%11111100 ; 100 ; -455555- ; clc + tax ; 102 +decode4: lda $00 ; 105 ; ----4444 -> $x0, partial + adc .lobyte(DECODETAB5),x ; 109 ; -455555- -> $xy, partial + pha ; 112 + lax VIA2_PRA ; 116 121 128 132 ; 66677777 ; 4 - %11: [104..129], %10: [112..139], %01: [120..149], %00: [128..159] + alr #%01000000 ; 118 ; --6----- ; clc + tay ; 120 + adc DECODETAB7,x ; 124 130 137 141 ; 66677777 -> $yx, partial, clv + ;[130 140 150 160] +densityswt: bvs readloop11 ; 3 2 2 2 ; is changed to bvc * for bit-rates %10, %01, %00 + bvs readloop11 ; 3 5 5 5 ; branch to readloop[00|01|10|11] +bitrateswt = * - 1 +READLOOPBASE = * + bvs readloop11 ; 3 + bvs readloop11 ; 3 + bvs readloop11 ; 3 + jmp readloop11 ; 3 + + .byte ___; padding + +findtrknum: lda #SCN; number of attempts on initial current track retrieval + sta SUCCESSCOUNT; reset attempt counter +: jsr getblkscan; any sector, no sector link sanity check, store ID +bcctofndte: bcc tofndtrerr + cmp CURRTRACK41 + sta CURRTRACK41 + bne findtrknum; on different track number, retry with same bit-rate + lda #.lobyte(:+ - 1) + sta STACKBUFFER + 2; return address lo + jsr readblock +: bcc bcctofndte + dec SUCCESSCOUNT; accept current track number only after some consecutive successful attempts + bne :-- + + jmp toidleloop + + ; * = $0100 + + .byte 0 + .byte 0; number of blocks (low-byte) of file #8 in loaded dir block + .byte 0; header sector number +stackbottom: + .assert stackbottom >= (STACK + 3), error, "***** 1541 stack below $0103. *****" + .assert stackbottom <= (STACK + 3), error, "***** 1541 stack above $0103. *****" + + .word 0; rawtoser etc. return address + .word 0; getblock etc. return address +stackend: +topofstack41 = stackend - 1 + + .assert stackend >= (STACK + 7), error, "***** 1541 top of stack below $0107. *****" + .assert stackend <= (STACK + 7), error, "***** 1541 top of stack above $0107. *****" + +stepperfix: ; here, the drive was apparently reset immediately before running the loader - + ; step down a track: this works normally if the stepping bits are congruent with the stepper motor. + ; however, it may happen that the bits are misaligned (opposite to the actual stepper position, bit 1 + ; reversed), this alone does not move the head but stepping makes it go into the direction opposite to + ; the one desired when moving. the stepping down two halftracks will actually step up and step down one + ; halftrack each and thus will end up on the same track as before, but align the stepper bits to the motor. + + ldy #2 + sty CURRTRACK41 + dey + jsr trkseek41 + +stepperok: lda #OPC_STA_ZPX + sta initlinklp + ldy #DECTABOFFS +: lda decodetab,y + sta DECODETABLE,y + iny + bne :- + beq findtrknum; jmp + + .byte 0; padding + +DECTABOFFS = $22 +decodetab = * & $ff00 + +decoffs0 = $2d +decoffs1 = $00 +decoffs2 = $00 +decoffs3 = $01 +decoffs5 = $01 +decoffs7 = $01 + +DECODETAB0 = DECODETABLE + decoffs0 +DECODETAB1 = DECODETABLE + decoffs1 +DECODETAB2 = decoffs2 +DECODETAB3 = DECODETABLE + decoffs3 +DECODETAB5 = decoffs5 +DECODETAB7 = DECODETABLE + decoffs7 + +BRANCHTAB = DECODETABLE + $92 + + .assert .lobyte(*) >= DECTABOFFS, error, "***** 'decodetab' data is below in-page offset $22. *****" + .assert .lobyte(*) <= DECTABOFFS, error, "***** 'decodetab' data is above in-page offset $22. *****" + +BR0 = .lobyte(readloop00 - READLOOPBASE) +BR1 = .lobyte(readloop01 - READLOOPBASE) +BR2 = .lobyte(readloop10 - READLOOPBASE) +BR3 = .lobyte(readloop11 - READLOOPBASE) + + .byte $b4, $b0, $ba, $ba, $bc, $b8 + +senddone: bcc donesend; jmp + + .byte $6e, $6f, $67, $d3, $6a, $6b + .byte $63, $b0, $26, $6d, $65, $2a, $60, $69, $61, $6c, $66, $6c, $64, $1a, $62, $68 + .byte $9c, $99, $b5, $b1, $b7, $b3, $b6, $b2, $f3, $e1, $5e, $5f, $57, $7a, $5a, $5b + .byte $53, $e0, $c4, $5d, $55, $6a, $50, $59, $51, $e8, $56, $5c, $54, $5a, $52, $58 + .byte $65, $bf, $bd, $b9, $bf, $bb, $be, $28, $e1, $e5, $4e, $4f, $47, $ba, $4a, $4b + .byte $43, $e4, $ee, $4d, $45, $aa, $40, $49, $41, $ec, $46, $4c, $44, $3a, $42, $48 + +donesend: lda #ATNA_OUT | CLK_OUT; drive busy, entry + sta (.lobyte(V1B - $ef),x); VIA1_PRB + bcs fileclosd2 +fileclosed: +waitsent: lda (.lobyte(V1B - $ef),x); VIA1_PRB + bpl waitsent + bcs donesend + + sei; disable watchdog + + .byte $fa; nop + + lax NUMCONTIGUOUSBLOCKS; ok: $00, file not found: $ff + .byte OPC_STA_ABS; sta $a2e2 + + .byte $e2, BR0; nop zp, but older 1541U firmwares do not emulate this correctly + + bne checkfnf + + .byte $ea; nop + + dec prpnxtfjmp; OPC_EOR_ABS -> OPC_JMP_ABS + + .byte $ea; nop + + ldx FILEDIRBUFFERINDEX; prepare next file, only after successful load + lda FILENAMEHASHVALSLO + 1,x + ldy FILENAMEHASHVALSHI + 1,x + jmp setnextfile; cache first block of next file + +toblksent: ldy FILETRACK + jmp blocksent + + .byte $ef, $2e, $2f, $27, $9a, $2a, $2b + .byte $23, $e6, BR1, $2d, $25, $8a, $20, $29, $21, $ee, $26, $2c, $24, $0a, $22, $28 + +checkfnf: inx; file not found: $ff -> $00 + bne toblksent + + SKIPBYTE +fileclosd2: tya; fileclosed: FILEDIRBUFFERINDEX, ok: >= 0, file not found: $ff + tax; checkfnf: file not found: $00 -> $ff + jmp mayfadeled; with N = 1, force fading off LED + + .byte $e9, $1e, $1f, $17, $da, $1a, $1b + .byte $13, $e3, BR2, $1d, $15, $ca, $10, $19, $11, $eb, $16, $1c, $14, $4a, $12, $18 + +finishload: sty NUMCONTIGUOUSBLOCKS; entry, ok: $00, file not found: $ff + tya; status + tax + dex; stx BLOCKSIZE + iny; y < BLOCKSIZE to send over one byte, ok: $01 < $ff, file not found: $00 < $fe + jmp sendstatus + + .byte $ed, $0e, $0f, $07, $94, $0a, $0b + .byte $03, $e7, BR3, $0d, $05, $dc, $00, $09, $01, $29, $06, $0c, $04, $92, $02, $08 + + ; * = $0200 +DECODETAB6: +bitrates: .byte .lobyte(~(%11 << BITRATE_SHIFT)); tracks 30+, 17 sectors + .byte .lobyte(~(%10 << BITRATE_SHIFT)); tracks 25-30, 18 sectors + .byte .lobyte(~(%01 << BITRATE_SHIFT)); tracks 18-24, 19 sectors + .byte $07 + .byte .lobyte(~(%00 << BITRATE_SHIFT)); tracks 1-17, 21 sectors + + .byte $90, $60, $80, $1f, $90, $60, $80, $69, $90, $60, $80 + + ; the raw <-> serial mapping swaps bits 0 and 3 of the lower nibble and + ; XORs the result with $7f, so it is the same for both encoding and decoding + +sertorawdt: sta TRACKLINKTABLE,x +sertorawd: dey +sertorawr: lda (STK),y; STACK +sertoraw41: +rawtoser: bit _09 + beq :++ +: eor #%00001001 + bit _09 + beq :- +: eor #%01111111 + sta HEADERTRACK; = BLOCKINDEX_STATUS = SPECSTARTSECTOR + rts + + .byte $00, $e0, $60, $fe, $00, $e0, $60 + +trkseek41: .byte OPC_LDA_IMM; lda #0 + + .byte $00, $e0, $60; cpx #$60 + +trackseekx: ldx #$80 | (MINSTEPSPEED + 1) + sax CURRSTEPSPEEDLOW; stores $0x + jsr setbv2b41 +trackstep: ; when single-stepping, CURRSTEPSPEEDLOW = decode4 + 1 (----4444) is not reset: it's assumed to be $0x + cpy CURRTRACK41 + beq setbitrate; branch if on same track + +nexttrack: bcc :+ + inc CURRTRACK41; move up (inwards) + SKIPWORD +: dec CURRTRACK41; move down (outwards) +halftrkdwn: lda #MOTOR / 4 + rol + asl; $04: move down (outwards), $06: move up (inwards) +halftrack: sta ERRORCOUNT; will be left at $84/$86, $40 is too little: would cause stepping when spinning up + eor VIA2_PRB + ;clc + jsr exectrseek + cpx #($80 | SINGLESTEPSPEED) - 1 + beq nostepwait; stepping to adjacent track: branch when second half-track step has been issued + + stx TIMER; reset track-step timer + txa +headaccl: cmp #$80 | MAXSTEPSPEED + beq noheadacc + pha + ;sec + lda CURRSTEPSPEEDLOW + sbc #STEPPERACC + sta CURRSTEPSPEEDLOW + pla + sbc #0 + +noheadacc: cpx TIMER + beq noheadacc; wait until the counter hi-byte has decreased by 1 + dex + bmi headaccl + tax + +nostepwait:;sec + lda #$80 - 1 + adc ERRORCOUNT +seekswitch: bmi halftrack; set to rts when stepping a single half-track during initialisation + cpy CURRTRACK41 + bne nexttrack; branch if not on destination track + +initlinktb: lsr CLEARSECTORLINKTABLE + bcs setbitrate + +initlink41: lda #$ff; sector link unknown + ldx #MAXNUMSECTORS - 1 +initlinklp: lda SECTORLINKTABLE41,x; is changed to sta SECTORLINKTABLE41,x after init, sector links are unknown + dex + bpl initlinklp + sta LOADEDSECTOR + + ; bit-rates: + ; tracks 31+ (17 blocks): %00 - sector interleave 3 (lowest density, slowest clock, innermost tracks) + ; tracks 25-30 (18 blocks): %01 - sector interleave 3 + ; tracks 18-24 (19 blocks): %10 - sector interleave 3 + ; tracks 1-17 (21 blocks): %11 - sector interleave 4 (highest density, fastest clock, outermost tracks) + +setbitrate: lax _E0; $e0 = SYNC_MARK | BITRATE_MASK, INDEXSPECULATIVE + ora (.lobyte(V2B - $e0),x); VIA2_PRB + ;ldy CURRTRACK41 + jsr getnumscts + inx + stx NUMSECTORSONTRACK + + SKIPWORD +motrledoff: and #255 - (BUSY_LED | MOTOR) + ldy #0 + beq store_via2; jmp + +fadeled: ldx #255 - BUSY_LED + lda (.lobyte(V2B - (255 - BUSY_LED)),x); VIA2_PRB + ldy LEDSTATE41 + beq motrledoff +: dey + bne :- + + .assert .hibyte(*) = .hibyte(:-), error, "***** Page boundary crossing in LED fade loop. *****" + + sax (.lobyte(V2B - (255 - BUSY_LED)),x); VIA2_PRB + ldy LEDSTATE41 +: iny + bne :- + + .assert .hibyte(*) = .hibyte(:-), error, "***** Page boundary crossing in LED fade loop. *****" + + .assert * >= $02a9, error, "***** 1571 watchdog IRQ vector in 1541 drivecode located below $02a9. *****" + .assert * <= $02a9, error, "***** 1571 watchdog IRQ vector in 1541 drivecode located above $02a9. *****" + + .word uninstall; uninstall = cmp $04, relevant for option ONLY_1541_AND_COMPATIBLE + + .assert .lobyte(uninstall) = OPC_CMP_ZP, error, "***** Invalid 1571 IRQ vector optimisation. *****" + + dec LEDSTATE41 + +setbv2b41: ora VIA2_PRB; setbv2b41 must not clobber y, see trkseek41 +turnmotron: ora #MOTOR +store_via2: sta VIA2_PRB; store_via2 must not clobber y, see motrledoff/exectrseek + rts + +dataread: ; read and decode ID0 (header) or checksum (data) + ;ldx #0 + lda (V2A,x) ; VIA2_PRA ; 63 65 72 76 ; 3:33334444 ; 2 - %11: [52..77], %10: [56..83], %01: [60..89], %00: [64..95] + ror ; ; 33333LHL:H + tay + lda DECODETAB3 - 2,y ; ; 33333--- -> $0x + ldy decode2 + 1 ; ; --22222- + eor DECODETAB2,y ; ; --22222- -> $x0 + tay ; header: ID041, data: checksum +: eor STACK,x ; header: ID1 track sector checksum + inx + cpx #4 ; = .lobyte(stackend - 3) + bne :- + eor checksum + 1 ; is 0 with header + eor #$80 + cmp #$ff ; c = 1: checksum matches + txs + + bit LOADEDSECTOR + bpl blockread + + ; process block header + + bcc chksumerr + + pla; STACK + 5, return address lo + sta STACKBUFFER + 2 + pla; STACK + 6, return address hi + sta STACKBUFFER + 3 + txs; $04 + + ; header: checksum sector track ID1 ID0 + lda (.lobyte(STK - $04),x); STACK, ID1 + cpy ID041 + bne :+ + cmp ID1 + beq :++ +: ldx REQUESTEDSECTOR41; set ID only when reading the BAM sector + +IDMM_ARG = idmismatch - (idmmbranch + 2) +idmmbranch: .byte OPC_BIT_ZP, .lobyte(IDMM_ARG); is changed to bne idmismatch after init + + .assert IDMM_ARG >= -128, error, "***** idmismatch branch too far. *****" + .assert IDMM_ARG <= 127, error, "***** idmismatch branch too far. *****" + + sty ID041 + sta ID1 + +: ldy #2 + jsr sertorawr; STACK + 2, sector + cmp NUMSECTORSONTRACK; check if sector number is within range of + bcs chksumerr ; the allowed sector numbers for the current track + sta LOADEDSECTOR + + jsr sertorawd; STACK + 1, track + ;sta HEADERTRACK + beq chksumerr + cmp #MAXTRACK41 + 1 + bcs chksumerr + +trkchkswtc: sec; is changed to lax LOADEDSECTOR after init + rts + + eor REQUESTEDSECTOR41 + beq readblock; branch if requested sector + bpl findblkhdr; specific sector requested but not reached yet + + ; negative value: no specific sector requested, out-of-order sector fetch + + ;ldx LOADEDSECTOR + lda INDEXTABLE,x + ldy SECTORLINKTABLE41,x + ldx #255 - (BLOCKPENDING | INDEXSPECULATIVE) + sax CURRBLOCKINDEX + + asl + bcs :+; branch if block index not known nor speculated + ora #255 - (BLOCKPENDING * 2); with positive INDEXTABLE value, branch if the block has already been loaded into the host's memory (BLOCKPENDING not set) + tay +: iny; with negative INDEXTABLE value, block index is unknown: branch to findblkhdr if linked sector is known (the block has been loaded before already) + bne findblkhdr + + ; skip this block if the following block is next in the stream + ldx NEXTCONTIGUOUSBLOCK + bne :+ + ldx NUMSECTORSONTRACK +: dex + cpx LOADEDSECTOR + beq findblkhdr + +readblock: ldy #%01010101;11 = $07, $55 + jsr waitsyncdb ; 17 ; returns with x = $fe for inx : txs + inx ; 19 + bvc readblkdat ; 22 + +chksumerr: ldx #ERT + lda (.lobyte(V2B - ERT),x); VIA2_PRB + anc #BITRATE | BUSY_LED | TRACK_STEP; anc is okay, as this code is not reached with 1541U +chksumswtc: lda ERRORCOUNT; is changed to dec ERRORCOUNT after init +idmismatch: bne checkchg + + ; with repeated checksum errors, the r/w head might have landed between tracks or on the wrong track + stx ERRORCOUNT + ;clc + sbc #(1 << BITRATE_SHIFT) - 1; cycle to the next bit-rate (denser until wrap) +exectrseek: adc #$03; and step down half a track on bit-rate wrap + jmp turnmotron; checksum error: z = 0, c = 0 + +blockread: ; LINKTRACK41 must not be 0, see nostep + ;stx LINKTRACK41; = decode2 + 1, which is non-0 at this point + + ; swap stack with loaded data + ;ldx #4 +: ldy stackbottom - 1,x + lda STACKBUFFER - 1,x + sta stackbottom - 1,x + sty STACKBUFFER - 1,x + dex + bne :- + + .assert .hibyte(*) = .hibyte(:-), error, "***** Page boundary crossing in swap stack <-> data loop. *****" + + bcc chksumerr; branch on checksum mismatch + + lax HEADERTRACK + eor CURRTRACK41 + tay +sanitychsw: beq sanitychck - 1; is changed to beq sanitychk after init + + ; track error + ldy CURRTRACK41 + stx CURRTRACK41 + inc CLEARSECTORLINKTABLE; is reset in trkseek41 + +getblktrsk: jsr trkseek41; stores the number of blocks on the current track in NUMSECTORSONTRACK +getblkscan: +findblkhdr: lax FE; $fe + sta LOADEDSECTOR; reset header/data flag to header + sta (.lobyte(TI1 - $fe),x); TIMER, reset sync time-out + + ldy #%01010010;01 = $08, $52 +: jsr waitsynchb ; 17 + bvs :- ; 19 + ldx #3 ; 21 + +readblkdat: bvc * ; 2 + + .assert .hibyte(*) = .hibyte(* - 2), error, "***** Page boundary crossing in bytesync loop. *****" + + alr #%11000001 ; 4 ; --------:3 + bne chksumerr ; 6 +denstyhalf: bvs :+ ; 9 8 ; bit-rate %11: bvs (branch taken), bit-rates %10, %01, %00: bvc (branch not taken) + pha ; 11 +: txs ; 11 13 + jmp readdata ; 14 16 + + ;lda VIA2_PRA ; 21 23 ; 3:33334444 ; 2 - cycle 21 is -4 in [0..25], cycle 23 is -4 in [0..27] + ;lda VIA2_PRA ; 44 46 ; 45555566 ; 3 - cycle 44 is -7 in [26..51], cycle 46 is -9 in [32..55] + ;lax VIA2_PRA ; 65 67 ; 66667777 ; 4 - cycle 65 is -12 in [52..77], cycle 67 is +3 in [64..83] + ;adc DECODETAB7,x ; 73 75 ; 66667777 ; cycle 73 is -4 in [52..77], cycle 75 is -8 in [64..83] + +getblkbam = * - 2; readdata = $00ba = lax #0: read sector 0 of dir track + + .assert .lobyte(readdata) = OPC_LAX_IMM, error, "***** Invalid getblkbam optimisation. *****" + +getblkdir: ldy #DIRTRACK +getblock41: sta REQUESTEDSECTOR41 + bpl getblktrsk; jmp + +linkerror: ; invalidate index + asl INDEXTABLE,x + bmi checkchg; branch if INDEXSPECULATIVE is set: clear index, set MSB + ror INDEXTABLE,x; restore non-speculative index + + SKIPWORD; skip to NUMFILES = sei +: sec ; refill directory buffer when disk has been changed + ror NUMFILES ; NUMFILES = $78 = OPC_SEI +readerror: ; sync timeout or invalid track/sector link +checkchg: ldx #.lobyte(stackend - 3); $04 + txs + lax (V2B - $04,x); VIA2_PRB, check light sensor for disk removal + eor DISKCHANGEBUFFER + stx DISKCHANGEBUFFER + alr #WRITE_PROTECT; clc + bne :- + ; read error: z = 1, c = 0 + rts + +sanitychck:;ldy #0 + ;sec + ldx LOADEDSECTOR + lda SECTORLINKTABLE41,x + bpl blkreturn; branch if valid sector link to this track known already, no sanity check required, save some + ; cycles to catch next block in time when not transferring the currently loaded block + jsr sertorawd + sta LINKTRACK41 + ;sta TRACKLINKTABLE,x + + jsr sertorawdt; sector link or block size + sta LINKSECTOR41 + + ;ldy #$fe + sty SECTORLINKTABLE41,x; mark block as loaded (linked sector is known) + + ; sector link sanity check + cmp #2 + ldy LINKTRACK41 + beq chklastbsz; branch if file's last block + + cpy #MAXTRACK41 + 1; check whether track link is within the valid range + bcs linkerror + +getnumscts: ldx #21 - 1; number of blocks minus one + cpy #18 + bcc :+; bit-rate $60 + ldx #19 - 1 + cpy #25 + bcc :+; bit-rate $40 + dex; 18 - 1 + cpy #31 + bcc :+; bit-rate $20 + dex; 17 - 1 +: and bitrates - (17 - 1),x + bmi return + + cpx LINKSECTOR41; check whether sector link is within the valid range +chklastbsz: ; branch if invalid block size (0..1 = 1..2 bytes) + bcc linkerror; branch if sector number too large + + ;lda LINKSECTOR41 ; loaded block's sector link sector number or block size + ldx LOADEDSECTOR ; return the loaded block's sector number + ;ldy LINKTRACK41 + cpy CURRTRACK41 + beq sametrack + asl ; must not be $ff + ora #%10000000; link is not to same track +sametrack: cmp LOADEDSECTOR ; block must not link to itself + beq linkerror ; (but larger cycles are not detected) + sta SECTORLINKTABLE41,x ; set sector link + sec; operation successful +blkreturn: rts + +readmod: ldx #%11100000 + stx densty11sw + axs #$70 + lda BRANCHTAB - $10,x + sta bitrateswt + cpx #%01110000; OPC_BVS, bit-rate %11 + lda #BR3 + 2 + bcs :+ + ldx #OPC_BVC; bit-rates %10, %01, %00 + lda #$fe; bvc * +: stx densityswt + sta densityswt + 1 + ror densty11sw; bit-rate %11: $f0 = OPC_BEQ, bit-rates %10, %01, %00: $70 = OPC_BVS + stx denstyhalf + +waitsyncdb: ldx #%11111110 + lda (.lobyte(TI1 - $fe),x) ; TIMER + beq readerror +waitsynchb: lda (.lobyte(V2B - $fe),x) ; VIA2_PRB, wait for SYNC + bmi readmod + + lda (.lobyte(V2A - $fe),x) ; VIA2_PRA, clear latch + clv + tya ; header: $52, data: $55 + bvc * ; 2 + cmp (.lobyte(V2A - $fe),x) ; 8, VIA2_PRA + bne return ; 11, 17 + ror + arr #%10000000 ; header: %01000000, data: %11000000 + clv + bvc * ; 2 + eor VIA2_PRA ; 6 ; 11222223, reads %-H222223 (header) or %HH222223 (data) + clv ; 8 + sax decode2 + 1 ; 11 ; --22222- +return: rts ; 17 + + ; validate block indices according to currently-known links +idxloop41: tax; link sector + iny; block index + tya + eor INDEXTABLE,x + bmi confirmidx; branch if index was neither known nor speculated until now + eor #BLOCKPENDING | INDEXSPECULATIVE + beq confirmidx; branch if not-yet-loaded speculative index matches + eor #BLOCKPENDING + beq confirmspc; branch if already-loaded speculative index matches +speculinit: clc ; mis-speculated block index detected +confirmidx: tya + ora #BLOCKPENDING +confirmspc: sta INDEXTABLE,x +specmanage: lda SECTORLINKTABLE41,x; linked sector + bpl idxloop41; branch if there is a linked sector on the same track + + .assert .hibyte(*) = .hibyte(idxloop41), error, "***** Page boundary crossing in idxloop41 *****" + + stx MAXCONFIRMEDBLKIDXSEC + sty MAXCONFIRMEDBLOCKINDEX + + lda #255 - BLOCKPENDING + SKIPWORD +rspeculate: ldy SPECWRAPSECTORINDEX + stx SPECSTARTSECTOR + bcc :+; branch if mis-speculated block index detected + + ; on first track, respeculate from last speculative index when it now has a known link + lax SPECWRAPSECTOR + ora SECTORLINKTABLE41,x + bmi specabort + lda #255 - (BLOCKPENDING | INDEXSPECULATIVE) + cpy SPECWRAPSECTORINDEX + bcc rspeculate + clc +: + sta CLEARINDEXMASK; = SPECWRAPSECTOR + + ; mis-speculated block indices detected: rebuild speculative block index table + + ;clc; minus one block: do not transfer alleged final block, as it may be mis-speculated + ; and load beyond the actual file end address due to possibly being larger than the file's final block + ldx FILEDIRBUFFERINDEX + lda NUMBLOCKS,x + sbc BLOCKINDEXBASE + bcc specabort; limit to alleged number of file blocks + cmp NUMSECTORSONTRACK + ldx NUMSECTORSONTRACK + bcc :+ + txa +: sta GENSPECLIMIT + + sty CLEARIDXMIN; = SPECWRAPSECTORINDEX +clearidxtb: lda CLEARINDEXMASK + and INDEXTABLE - 1,x + cmp CLEARIDXMIN + beq :+ + bcc :+ + ror INDEXTABLE - 1,x +: dex + bne clearidxtb + + .assert .hibyte(*) = .hibyte(clearidxtb), error, "***** Page boundary crossing in clear index table loop. *****" + + ldx SPECSTARTSECTOR +speculate: iny + cpy GENSPECLIMIT + bcs specdone + stx SPECWRAPSECTOR + lda SECTORLINKTABLE41,x + bpl havenewidx; branch if there is a linked sector on the same track + txa + ;clc + adc SPECULATIVEINTERLEAVE +havenewidx: tax + sec + sbc NUMSECTORSONTRACK + bcs specwrap + lda INDEXTABLE,x + bpl specsectkn + tya + ora #BLOCKPENDING | INDEXSPECULATIVE + sta INDEXTABLE,x + sty SPECWRAPSECTORINDEX + bne speculate; jmp + + .assert .hibyte(*) = .hibyte(speculate), error, "***** Page boundary crossing in speculate loop. *****" + +specsectkn: inx + txa +specwrap: ldx BLOCKINDEXBASE; for the first file track, only speculate until wrap and stop on already-taken speculative sectors, + bne havenewidx ; as going on would likely lead to much mis-speculation and ultimately lower speed +specabort: rts + + FNAMEHASH 1541 + +wdogentr41: +: inc CURRTRACK41 +uninstall = * - 1; CURRTRACK41 = $7a = nop + ldy #18; ROM dir track + jsr trkseek41 + ;lda VIA2_PRB + bit _02; the stepper bits are set to %00 after reset: ensure full-track + bne :- ; stepper alignment (may still be off by a half-track) + + dec reset; OPC_ADC_ABS -> OPC_JMP_ABSI + + ;ldy #0 + dey + sty LEDSTATE41 + +mayfadeled: bpl :+ + +fadeledidl: jsr fadeled + bne fadeledidl + +reset: adc RESET_VECTOR + + SKIPBYTE; prpnxtfjmp = eor ($06,x) + ; jump from prpnxtfjmp when drive has cached next file's first block +prpdnxtfil: inc prpnxtfjmp; OPC_JMP_ABS -> OPC_EOR_ABS + +idleloop41: jsr fadeled +: jsr checkchg; check light sensor for disk removal + + ;lda #0 + tax + lda (V1B,x); VIA1_PRB + bpl uninstall + lsr + bcs idleloop41; wait until there is something to do + + sta ERRORCOUNT; reset to >= $40, as spinning up the motor would yield errors + + FILENAME = FILENAME41 + GET_FILENAME 1541 + + ; matches against hash of filename in FILENAMEHASHLO/HI + NUMFILES41 = NUMFILES + findfile41 = findfile + FIND_FILE 1541 + + ldy DIRSECTORS,x + sty FILESECTOR41; = NEXTCONTIGUOUSBLOCK + ldy DIRTRACKS,x +loadfile41: +filenotfnd: ; y = $ff = diskio::status::FILE_NOT_FOUND + ;lda #0 + sta PREVBLOCKINDEXPLUS1 + sta NUMCONTIGUOUSBLOCKS + +blocksent: bmi blockloop + bne trackloop + + ; loading is finished + ; y = $00 = diskio::status::OK + jmp DECODETABLE + .lobyte(finishload) + +TRACKLINKTABLE: ; will be overwritten with TRACKLINKTABLE (MAXNUMSECTORS = 21 bytes) + .byte 0; padding + +toidleloop: lda #OPC_BNE + sta idmmbranch + lda #OPC_LAX_ZP + sta trkchkswtc + lda #LOADEDSECTOR + sta trkchkswtc + 1 + lda #OPC_DEC_ZP + sta chksumswtc + +FILENAMEHASHVALSLO = * ; will be overwritten with FILENAMEHASHVALSLO (DIRBUFFSIZE = 11 bytes) + + inc sanitychsw + 1 + ldy CURRTRACK41 + jsr initlinktb + jsr getblkdir; accu contains a negative number for any sector + +NUMBLOCKS: ; will be overwritten with NUMBLOCKS (DIRBUFFSIZE = 11 bytes) + + jmp idleloop41 + + .byte 0, 0, 0, 0, 0, 0, 0, 0; padding + + brk; spare + +trackloop: sta BLOCKINDEXBASE + + lda #BUSY_LED | MOTOR + jsr trackseekx + + ; accu is negative with INDEXSPECULATIVE set, sector indices are unknown + ;ldx NUMSECTORSONTRACK + inx +trackinit: sta INDEXTABLE - 2,x + dex + bpl trackinit; sets REQUESTEDSECTOR41 = FILETRACK = INDEXTABLE - 1 with x = 1 and LEDSTATE41 = INDEXTABLE - 2 with x = 0 + + .assert .hibyte(*) = .hibyte(trackinit), error, "***** Page boundary crossing in trackinit loop. *****" + + ldx FILESECTOR41; = NEXTCONTIGUOUSBLOCK + cpx NUMSECTORSONTRACK; illegal sector is checked only here because NUMSECTORSONTRACK + bcs findloop ; is only here set correctly for the file's starting track + + ; set non-speculative block indices according to known links, build initial speculative block index table for this track + ;ldx NEXTCONTIGUOUSBLOCK + ;ldy #0; initial block index + jsr speculinit + + lda FILESECTOR41; = NEXTCONTIGUOUSBLOCK + cmp LOADEDSECTOR + beq servecache + + SKIPWORD +idxinvalid:;sec + ror INDEXTABLE,x; clear index, set MSB + +blockloop: jsr getblkscan + bcc blockloop; branch if block fetch not successful + + ldy CURRBLOCKINDEX + bmi blockloop; branch if block conceivably not belonging to the file + beq refuspecil; if first file block on track: skip PREVBLOCKINDEXPLUS1 check + + ldy PREVBLOCKINDEXPLUS1; first file block must be loaded first in order to know the load address to be able to place successive + beq blockloop ; blocks in host memory, skip loaded blocks until the required one is read + + ; determine speculative interleave + ;sec + ;lda LINKSECTOR41 + tax + sbc LOADEDSECTOR; determine likely interleave + ; validate speculative interleave + beq refuspecil + cmp #MAXINTERLEAVE + 1 + bcs refuspecil + ; only accept interleave if distances to this block and the next match + tay + adc LINKSECTOR41 + cmp NUMSECTORSONTRACK + bcc :+ + sbc NUMSECTORSONTRACK +: cmp SECTORLINKTABLE41,x + bne refuspecil - 1 + sty SPECULATIVEINTERLEAVE; SPECULATIVEINTERLEAVE = $38 = OPC_SEC +refuspecil: + ldx MAXCONFIRMEDBLKIDXSEC + ldy MAXCONFIRMEDBLOCKINDEX + ;sec; set no mis-speculation detected flag + jsr specmanage +servecache: + sec + ldx LOADEDSECTOR + + .assert .lobyte(*) = OPC_EOR_ZPXI, error, "***** Invalid prpnxtfjmp optimisation. *****" + +prpnxtfjmp: eor prpdnxtfil; is changed to jmp prpdnxtfil when caching the next file's first block + + lda #255 - BLOCKPENDING + and INDEXTABLE,x + sta INDEXTABLE,x + + ;sec + sbc #INDEXSPECULATIVE + ; c = 1 if index speculative, 0 otherwise + + bcc sendblock; branch if block index is not speculative + + sta CURRBLOCKINDEX; speculative index might have changed + lda MAXCONFIRMEDBLOCKINDEX + cmp CURRBLOCKINDEX; do not transfer block if another block has been confirmed to have the same or a + bcs idxinvalid ; higher index (this block's index is wrong or wouldn't be speculative any more) + +sendblock: lda NEXTCONTIGUOUSBLOCK +: tax + ldy INDEXTABLE,x + cpy #MAXNUMSECTORS + bcs :+; branch if block has not been confirmed and transferred already + inc NUMCONTIGUOUSBLOCKS + lda SECTORLINKTABLE41,x + bpl :-; branch if there is a linked sector on the same track + + .assert .hibyte(*) = .hibyte(:-), error, "***** Page boundary crossing in contiguous blocks update loop. *****" + + alr #%00011111 * 2 + ;clc +: sta FILESECTOR41; = NEXTCONTIGUOUSBLOCK, first file sector on the linked track + bcs :+ + + ldy TRACKLINKTABLE,x + sty FILETRACK + beq nostep + + ; perform Shrydar Stepping (R)(TM) to minimise single-track stepping overhead: + ; nudge the R/W head in the right direction, then wait a few bycles while it gains momentum, + ; then enable the destination track's stepper magnet long before the head has reached the intermediate half-track's magnet, + ; relying on the head's inertia, then send over the block while the head keeps moving beyond the intermediate half-track's stepper magnet + ; and ultimately settles on the destination track. + ; sending the block over takes at least 72 bycles + + ldx #$80 | SINGLESTEPSPEED + jsr trackstep +nostep: + ; calculate block index in host memory + sec +: lda BLOCKINDEXBASE + adc CURRBLOCKINDEX + tax + sec + sbc PREVBLOCKINDEXPLUS1 + stx PREVBLOCKINDEXPLUS1 + + ldy #0 + cpy LINKTRACK41 + rol; last block: set lsb, clear lsb otherwise + jsr rawtoser + sta (SFE),y; STACK + $fe, block index and last block size/next contiguous block index flag + + lda NUMCONTIGUOUSBLOCKS + ldx #1 + asl LINKTRACK41 + bne :+ + ; handle file's last block + txa + ;clc + sbc LINKSECTOR41; the file's last block's length (last byte offset) + tax +: + ; accu: next contiguous block index/block size + dey; block: $00 -> $ff + +sendstatus: stx BLOCKSIZE + jsr rawtoser + ;sta BLOCKINDEX_STATUS + + ; restore block buffer on stack + ldx #4 +: lda STACKBUFFER - 1,x + sta stackbottom - 1,x + dex + bne :- + + ; send block ready signal and wait for the signal to begin transferring the block + lax EF; ~ATNA_OUT = $ef + sta (.lobyte(TI2 - $ef),x); TIMER2, poll two cascaded timers: an extra-long time-out period is needed since + sta (.lobyte(TI1 - $ef),x); TIMER, the host may still be busy decompressing a large chunk of data + + sax (.lobyte(V1B - $ef),x); VIA1_PRB, clear ATNA_OUT, set DATA_OUT now, before clearing CLK, so that there is a flank only on CLK (but not DATA) when signalling ready, + ; such that just one serial bus read can safely determine block ready (CLK clear) and drive present (DATA set) vs device not present (both clear) + sre (.lobyte(V1B - $ef),x); VIA1_PRB, block ready signal: %1xy01111 -> %11xy0011, DATA_OUT as signal of presence remains set regardless of ATNA_OUT state + +waitready: lda TIMER2LO; see if the watchdog barked + cmp TIMERLO +.if ::DISABLE_WATCHDOG + bmi :+ +: +.else + bmi timeout; and trigger watchdog on time-out +.endif + lda #CLK_IN + bit VIA1_PRB + bmi waitready; wait for ATN_IN clear + + .assert .hibyte(*) = .hibyte(waitready), error, "***** Page boundary crossing in block send wait loop. *****" + + bne closefile + + stx TIMER; reset watchdog time-out and clear possibly set IRQ flag +timeout: ENABLE_WATCHDOG + + lda BLOCKINDEX_STATUS + sax (.lobyte(V1B - $ef),x); VIA1_PRB + +sendloop: asl ; 2 + ora #ATNA_OUT ; 2 + ; = 17 + +: bit VIA1_PRB ; 4 + bpl :- ; 2 + sta VIA1_PRB ; 4 + ror ; 2 + alr #%11110000 ; 2 + stx TIMER ; 4 - reset watchdog time-out + ; = 18 + +: bit VIA1_PRB ; 4 + bmi :- ; 2 + sta VIA1_PRB ; 4 + lsr ; 2 + alr #%00110000 ; 2 + cpy BLOCKSIZE ; 3 + ; = 17 + +: bit VIA1_PRB ; 4 + bpl :- ; 2 + sta VIA1_PRB ; 4 + dey ; 2 + lda (STK),y ; 5 + ; = 17 + +: bit VIA1_PRB ; 4 + bmi :- ; 2 + sax VIA1_PRB ; 4 + bcs sendloop ; 3 + ; = 69 + + .assert .hibyte(*) = .hibyte(sendloop), error, "***** Page boundary crossing in sendloop. *****" + + bcc DECODETABLE + .lobyte(senddone); jmp + + .assert * >= $0700, error, "***** 1541 watchdog handler below $0700. *****" + .assert * <= $0700, error, "***** 1541 watchdog handler above $0700. *****" + + jmp uninstall + + ; will be overwritten with DIRTRACKS ($0703..$070d) and DIRSECTORS ($070e..$0718) +findtrkstp:;lda #BTR + sta BITRATECOUNT + dec ERRORCOUNT + bne :+ + asl seekswitch; OPC_BMI -> OPC_RTS + ;clc + ldx #$80 | (MINSTEPSPEED + 1) + jsr halftrkdwn + sta ERRORCOUNT + lsr seekswitch; OPC_RTS -> OPC_BMI +: jmp findtrknum + + .assert * <= (DIRSECTORS + DIRBUFFSIZE), error, "***** 'findtrkstp' code too large. *****" + +closefile: ldy FILEDIRBUFFERINDEX + bmi :+; branch if file not found: DATA_OUT remains set + + ; clear DATA_OUT otherwise + ;lda #CLK_IN + sta (.lobyte(V1B - $ef),x); VIA1_PRB + +: sec + bcs DECODETABLE + .lobyte(fileclosed); jmp + + .assert * <= (DECODETABLE + DECTABOFFS), error, "***** 1541 drive code too large. *****" + +dcodinit41: lda #ATNA_OUT | CLK_OUT; drive idle + sta VIA1_PRB + + ;ldx ROMOS_TRACK_DIFF + txa + ldx #.lobyte(topofstack41); $06 + txs + pha + + lda #T1_FREE_RUNNING | PA_LATCHING_ENABLE; watchdog IRQ: count phi2 pulses, 16-bit free-running, + sta VIA2_ACR ; enable port A latching to grab one GCR byte at a time rather + ; than letting the GCR bitstream scroll through than port A + ; (applies to 1541 and Oceanic OC-118, but not 1541-II) + lda #READ_MODE | BYTE_SYNC_ENABLE + sta VIA2_PCR + + ; timers/watchdog initialisation + ; for the long timeout between block-ready and block-send, use two arithmetically cascaded timers: + ; their periods differ, so their counters drift further apart every time either timer resets. + ; the effective timeout is reached as soon as the difference between the counters is >= 128, which for + ; the used periods' difference of 7 cycles with the counter periods of $ef00 and $ef07 is at least + ; floor(128 / 7) * $ef07 = 18 * $ef07 = 1,101,438 cycles at 1 MHz, i.e., roughly 1 second. + ; a few cycles are added to or subtracted from the effective timeout: added because as a counter reset + ; apparently takes 2 cycles, so the effective periods are $ef02 and $ef09, subtracted because the counters' + ; difference does not increase by 7 on counter $ef07 reset, but increases by 7 and then decreases by 2. + + lda #$07 + sta VIA1_T1L_L; VIA1 timer 1 (TIMER2) low-order latch + ldx #$00 + stx VIA2_T1L_L; VIA2 timer 1 (TIMER) low-order latch + stx VIA1_PRA; one MHz if running on 1570/71 with option ONLY_1541_AND_COMPATIBLE + + lda #IRQ_CLEAR_FLAGS | IRQ_ALL_FLAGS; $7f + sta VIA1_IER; no IRQs from VIA 1 + sta VIA2_IER; no IRQs from VIA 2 + lda #IRQ_SET_FLAGS | IRQ_TIMER_1 + sta VIA2_IER; timer 1 IRQs from VIA 2 + + lda (V1B,x); VIA1_PRB + and #DEVICE_NUMBER + ora getbytecmp41 + 1 + sta getbytecmp41 + 1 + + ; fade off the busy LED if lit + lda LEDSTATE41 + pha + lda (V2B,x); VIA2_PRB + sta DISKCHANGEBUFFER; store light sensor state for disk removal detection + and #BUSY_LED + beq :+ + dex +: stx LEDSTATE41 +: jsr fadeled + bne :- + pla + sta LEDSTATE41 + + pla + ;lda ROMOS_TRACK_DIFF + bne :+ + jmp stepperfix; motor on via trackstep +: lda #MOTOR + jsr setbv2b41 + jmp stepperok + +drvcodeend41: + +TRAMPOLINEOFFSET = $27; dgetrout - dinstall + 1 + + .org * - TRAMPOLINEOFFSET + +dinstall: sei + ldx ROMOS_TRACK_DIFF; $42 + txs + + lda #VIA_ATN_IN_INPUT | VIA_DEVICE_NUMBER_INPUT | VIA_ATNA_OUT_OUTPUT | VIA_CLK_OUT_OUTPUT | VIA_CLK_IN_INPUT | VIA_DATA_OUT_OUTPUT | VIA_DATA_IN_INPUT + sta VIA1_DDRB + lda VIA1_PRB + and #DEVICE_NUMBER + ora getbyteinstall + 1 + sta getbyteinstall + 1 + + lda #ATNA_OUT | CLK_OUT + sta VIA1_PRB +: lda VIA1_PRB; wait for ATN_IN set and DATA_IN clear + lsr + bcs :- + lda #ATNA_OUT + sta VIA1_PRB + + ldx #.lobyte(drvcodebeg41 - 1) +dgetrout: inx + + .assert * >= drvcodeend41, error, "***** 1541 trampoline too low in memory. *****" + + bne :+ + inc dgetputhi +: DRIVEGETBYTE 1541, getbyteinstall; there is no watchdog while installing +dgetputhi = * + 2 + sta a:.hibyte(drvcodebeg41 - 1) << 8,x + cpx #.lobyte(drvcodeend41 - 1) + bne dgetrout + dec drvcodebeg41 + bne dgetrout + + tsx; ROMOS_TRACK_DIFF + jmp dcodinit41 + +drvprgend41: + .assert * <= $0800, error, "***** 1541 drive code too large. *****" + + .reloc diff --git a/loader/src/drives/drivecode1571.s b/loader/src/drives/drivecode1571.s new file mode 100755 index 0000000..c712a1d --- /dev/null +++ b/loader/src/drives/drivecode1571.s @@ -0,0 +1,1336 @@ + +.include "cpu.inc" +.include "via.inc" +.include "cia.inc" + +.include "drives/drivecode-common.inc" + +CURRTRACK71 = $00 +MAXTRACK_A = $01 +LEDSTATE71 = $02; fixed: TWO +NUMSECTORSONTRACK = $03 +MAXTRACK = $04 +DISKCHANGEBUFFER = $05 +FILENAMEHASHLO = $06 +FILENAMEHASHHI = $07 +CHECKSUM = $08 +LINKSECTOR71 = CHECKSUM +ERRORCOUNT = $09 +CLEARSECTORLINKTABLE71 = $0a +TEMP = $0b +CURRSTEPSPEEDLOW = TEMP +GCRBUFFER = TEMP +BLOCKSIZE = TEMP +SPECSTARTSECTOR = TEMP +TRACKINC = $0c +HEADERTRACK = TRACKINC +GENSPECLIMIT = TRACKINC +LINKTRACK71 = $0d +FILESECTOR = $0e +FILEDIRBUFFERINDEX = $0f +BLOCKINDEXBASE = $10 +CURRBLOCKINDEX = $11 +ID0 = $12; fixed: DSKID +ID1 = $13; fixed: DSKID +PREVBLOCKINDEXPLUS1 = $14 +MAXCONFIRMEDBLOCKINDEX = $15 +MAXCONFIRMEDBLKIDXSEC = $16 +NUMCONTIGUOUSBLOCKS = $17 +NUMFILES71 = $18; fixed: OPC_CLC +NEXTCONTIGUOUSBLOCK = $19 +NEXTDIRBLOCKSECTOR = $1a +DIRBLOCKPOS = $1b +SPECULATIVEINTERLEAVE = $1c +SPECWRAPSECTOR = $1d; SPECWRAPSECTOR = CLEARINDEXMASK +CLEARINDEXMASK = SPECWRAPSECTOR +SPECWRAPSECTORINDEX = $1e +CLEARIDXMIN = SPECWRAPSECTORINDEX + +SECTORLINKTABLE71 = $1f; $15 = MAXNUMSECTORS bytes +LOADEDSECTOR = SECTORLINKTABLE71 + MAXNUMSECTORS + +FILETRACK = LOADEDSECTOR + 1; must be INDEXTABLE - 2: see trackinit +REQUESTEDSECTOR = FILETRACK + 1; must be INDEXTABLE - 1: see trackinit +INDEXTABLE = REQUESTEDSECTOR + 1; $15 = MAXNUMSECTORS bytes +TRACKLINKTABLE = INDEXTABLE + MAXNUMSECTORS; $15 = MAXNUMSECTORS bytes + +DIRBUFFER = TRACKLINKTABLE + MAXNUMSECTORS + +CYCLESTARTENDSECTOR = FILEDIRBUFFERINDEX + +FILENAME71 = INDEXTABLE; max. $10 bytes + +DIRBUFFSIZE = (enddirbuffer - DIRBUFFER) / 5 +DIRTRACKS = DIRBUFFER +DIRSECTORS = DIRTRACKS + DIRBUFFSIZE +NUMBLOCKS = DIRSECTORS + DIRBUFFSIZE +FILENAMEHASHVALSLO = NUMBLOCKS + DIRBUFFSIZE +FILENAMEHASHVALSHI = FILENAMEHASHVALSLO + DIRBUFFSIZE + +DIRBUFFSIZE71 = DIRBUFFSIZE +.export DIRBUFFSIZE71 + + .assert DIRBUFFSIZE >= 11, error, "***** Dir buffer too small. *****" + +BLOCKBUFFER71 = $0700 +TRACKOFFSET = $00 +SECTOROFFSET = $01 +TWOSIDEDOFFSET = $03 +BLOCKSOFFSET = $1e + +ROMOS_TRACK_DIFF = $42 + +DECGCRTAB10ZZZ432LO = $a00d; = $9f0d (GCRTBA) +;DECGCRTAB3210ZZZ4LO = $9f0f; GCRTBD +;DECGCRTAB0ZZZ4321HI = $9f1d; GCRTBE +DECGCRTAB210ZZZ43HI = $9f2a; GCRTBG +DECGCRTAB43210ZZZHI = $a00d; GCRTB1 +DECGCRTABZZ43210ZHI = $a10d; GCRTB2 +DECGCRTABZ43210ZZLO = $a20d; GCRTB3 +DECGCRTABZZZ43210LO = $a30d; GCRTB4 + +BINARY_NIBBLE_MASK = %00001111 + +MAXINTERLEAVE = 16 +MAXNUMSECTORS = 21 +NUMTRACKS_SINGLESIDED = 41 +NUMTRACKS_A = 35 +NUMTRACKS_B = NUMTRACKS_SINGLESIDED +MAXTRACK71 = NUMTRACKS_A + NUMTRACKS_B + +INDEXSPECULATIVE = %01000000 +BLOCKPENDING = %00100000 + +TIMER = VIA2_T1C_H + +ERR = $80; ERRORCOUNT reset value, $40 is too little: would cause stepping when spinning up +ERT = $20; ERRORCOUNT retry value + +BITRATECOUNT = 4; number of read attempts before bit-rate cycling on initial current track retrieval +SUCCESSCOUNT = 10; number of consecutive successful per-bit-rate attempts on initial current track retrieval + +INITIALSPECINTERLEAVE = 4 + + .org $c3 + +drvcodebeg71: + .byte .hibyte(drvcodeend71 - * + $0100 - $01); init transfer count hi-byte + +FINDTRACKN = INDEXTABLE + +bitratecnt = PREVBLOCKINDEXPLUS1 +successcnt = MAXCONFIRMEDBLKIDXSEC + +enddirbuffer: + +V2A: .word VIA2_PRA + +getbytewdg: lda #$ff +TIM = * + 1 + sta TIMER; reset watchdog time-out + ENABLE_WATCHDOG +V1B = getbyte + 1 +getbyte: DRIVEGETBYTE 1571, getbytecmp + rts + + ; for normal busy LED fading speed and correct head stepping speed +onemhz71: lda #.lobyte(~TWO_MHZ) +V1A = * + 1 + and VIA1_PRA +store_via1: sta VIA1_PRA + rts + +dofadeled: dey + bne dofadeled + + .assert .hibyte(*) = .hibyte(dofadeled), error, "***** Page boundary crossing in LED fade loop. *****" + +BUSY_LED_MASK = ~BUSY_LED + ldx #.lobyte(BUSY_LED_MASK) + sax (.lobyte(V2B - BUSY_LED_MASK),x); VIA2_PRB + ldy LEDSTATE71 +: iny + bne :- + + .assert .hibyte(*) = .hibyte(:-), error, "***** Page boundary crossing in LED fade loop. *****" +TWO = * + 1 + dec LEDSTATE71 +V2B = * + 1 +bsetv2b71: ora VIA2_PRB + + .assert * <= $0100, error, "***** V2B not in zeropage. *****" + + sta VIA2_PRB + rts + +fadeled: jsr onemhz71; 1 MHz so the LED fades at the same speed as on 1541 + +MOTOR_LED_MASK = ~(BUSY_LED | MOTOR) + ldx #.lobyte(MOTOR_LED_MASK) + lda (.lobyte(V2B - MOTOR_LED_MASK),x); VIA2_PRB + ldy LEDSTATE71 + bne dofadeled + + sax (.lobyte(V2B - MOTOR_LED_MASK),x); VIA2_PRB +notrkstep: rts + +trkseek71c: ldy CURRTRACK71 + stx CURRTRACK71 + lsr CLEARSECTORLINKTABLE71 + +trkseek71: lda #MOTOR; turn on the motor +trackseekx: jsr bsetv2b71 + ldx #$80 | (MINSTEPSPEED + 1) +trackstep: tya; destination track + beq notrkstep; don't do anything if invalid track + cmp MAXTRACK + bcs notrkstep; don't do anything if invalid track + cmp CURRTRACK71 + beq setbitrate; branch if on same track + + sec + lda CURRTRACK71 + sbc MAXTRACK_A + beq :+ + bcc :+ + sta CURRTRACK71; the current track is on the 2nd side, + ; temporarily store the 2nd side physical track number +: sec + tya; destination track + sbc MAXTRACK_A + beq :+ + bcs :++; branch if the destination track is on the 2nd side +: clc + tya; the destination track is on the 1st side +: pha + lda VIA1_PRA + and #.lobyte(~SIDE_SELECT) + bcc :+ + ora #SIDE_B +: +c1570fix1: sta VIA1_PRA + pla + sec + sbc CURRTRACK71 + sty CURRTRACK71 + beq initlinktb; branch if destination track reached + + ; do the track jump + ldy #$01; move up (inwards) + sty CURRSTEPSPEEDLOW + bcs :+ + eor #$ff; invert track difference + adc #$01 +halftrkdwn: ldy #$03; move down (outwards) +: sty TRACKINC + asl; half-tracks + tay + + jsr onemhz71 + +halftrack: stx TIMER; reset track-step timer + lda VIA2_PRB + anc #.lobyte(~(SYNC_MARK | MOTOR)); clc + adc TRACKINC + ora #MOTOR + sta VIA2_PRB + cpx #($80 | SINGLESTEPSPEED) - 1 + beq initlinktb; stepping to adjacent track: branch if second half-track step has been issued + + txa +headaccl: cmp #$80 | MAXSTEPSPEED + beq noheadacc + pha + ;sec + lda CURRSTEPSPEEDLOW + sbc #STEPPERACC + sta CURRSTEPSPEEDLOW + pla + sbc #$00 + +noheadacc: cpx TIMER + beq noheadacc; wait until the counter hi-byte has decreased by 1 + dex + bmi headaccl +seekswitch: tax + dey + bne halftrack + +initlinktb: lda CLEARSECTORLINKTABLE71 + bpl :++ +initlink71: lda #$ff; sector link unknown + ldx #MAXNUMSECTORS +: sta SECTORLINKTABLE71,x; sector links are unknown + dex + bpl :-; sets LOADEDSECTOR = SECTORLINKTABLE71 + MAXNUMSECTORS with x = MAXNUMSECTORS + +: lda #ERR + sta ERRORCOUNT + + ; bit-rates: + ; tracks 31-35/66+ (17 blocks): %00 - sector interleave 3 (lowest density, slowest clock, innermost tracks) + ; tracks 25-30/60-65 (18 blocks): %01 - sector interleave 3 + ; tracks 18-24/53-59 (19 blocks): %10 - sector interleave 3 + ; tracks 1-17/36-52 (21 blocks): %11 - sector interleave 4 (highest density, fastest clock, outermost tracks) +setbitrate: lda CURRTRACK71 + jsr getnumscts + stx NUMSECTORSONTRACK + ldy #0 + sta (V2B),y; VIA2_PRB + ; fall through + +twomhz: lda #BYTE_READY | TWO_MHZ; the accu must contain a negative number upon return + ora VIA1_PRA + jmp store_via1 + + BLOCKBUFFER = BLOCKBUFFER71 + NUMFILES = NUMFILES71 + FNAMEHASH 1571; must not be overwritten by watchdog IRQ/reset/custom code upload +readerrts = * - 1 + + ; * >= $0100 +stack: + .assert stack >= $0100, error, "***** 1571 stack too low in memory. *****" + + .word 0, 0, 0 +stackend: +topofstack71 = stackend - 1 + + .assert stackend < $0200, error, "***** 1571 stack too high in memory. *****" + +readheader: lda #%01000000 + ldy #$fc + +readdata: bit VIA1_PRA + bmi readdata + + eor VIA2_PRA ; 11222223 + sta GCRBUFFER + and #%11000000 + bne readerrts; z = 0 + +loaddata: bit VIA1_PRA ; 4 + bmi loaddata ; 6 + sta CHECKSUM ; 9 + lda GCRBUFFER ; 12 ; 11222223 + lsr ; 14 ; .1122222:3 + lda VIA2_PRA ; 18 ; 33334444 + pha ; 21 + ror ; 23 ; 33333444 + lsr ; 25 ; .3333344 + tax ; 27 + lda DECGCRTABZ43210ZZLO,x ; 31 ; ....3333 + ldx GCRBUFFER ; 34 ; 11222223 + ora DECGCRTABZZ43210ZHI,x ; 38 ; 22223333 + +: bit VIA1_PRA ; 4 + bmi :- ; 6 + sta BLOCKBUFFER71 + 0,y ; 11 + lda VIA2_PRA ; 15 ; 45555566 + sta GCRBUFFER ; 18 + asl ; 20 ; 4:5555566. + pla ; 24 ; 33334444 + rol ; 26 ; 33344444 + asl ; 28 ; 3344444. + tax ; 30 + lda DECGCRTABZZ43210ZHI,x ; 34 ; 4444.... + ldx GCRBUFFER ; 37 ; 45555566 + ora DECGCRTABZ43210ZZLO,x ; 41 ; 44445555 + +: bit VIA1_PRA ; 4 + bmi :- ; 6 + sta BLOCKBUFFER71 + 1,y ; 11 + lda #%00000011 ; 13 + axs #%00100000 ; 15 ; HHHLLL66 + lda VIA2_PRA ; 19 ; 66677777 + sta GCRBUFFER ; 22 + ora #%00011111 ; 24 ; 666HHHHH + axs #0 ; 26 ; 666LLL66 + lda DECGCRTAB210ZZZ43HI,x ; 30 ; 6666.... + ldx GCRBUFFER ; 33 ; 66677777 + ora DECGCRTABZZZ43210LO,x ; 37 ; 66667777 + sta BLOCKBUFFER71 + 2,y ; 42 + +: bit VIA1_PRA ; 4 + bmi :- ; 6 + ldx VIA2_PRA ; 10 ; 00000111 + eor BLOCKBUFFER71 + 1,y ; 14 + eor BLOCKBUFFER71 + 0,y ; 18 + eor CHECKSUM ; 21 + sta CHECKSUM ; 24 + lda DECGCRTAB43210ZZZHI,x ; 28 ; 0000.... + pha ; 31 + lda #%00000111 ; 33 + axs #%01000000 ; 35 ; HHLLLL111 + iny ; 37 + iny ; 39 + iny ; 41 + +: bit VIA1_PRA ; 4 + bmi :- ; 6 + lda VIA2_PRA ; 10 ; 11222223 + sta GCRBUFFER ; 13 + ora #%00111111 ; 15 ; 11HHHHHH + axs #0 ; 17 ; 11LLL111 + pla ; 21 ; 0000.... + ora DECGCRTAB10ZZZ432LO,x ; 25 ; 00001111 + sta BLOCKBUFFER71 + 0,y ; 30 + eor CHECKSUM ; 33 + iny ; 35 + beq loaddone ; 37 + jmp loaddata ; 40 + +getnumscts: tay + sec + sbc MAXTRACK_A + beq :+ + bcs getnumscts +: ldx #21; number of blocks + lda #SYNC_MARK | BITRATE_MASK; $e0 + ora ((V2B - 21),x); VIA2_PRB + cpy #18 + bcc :++; bit-rate $60 + ldx #19 + cpy #25 + bcc :+ ; bit-rate $40 + dex; 18 + and #.lobyte(~(%10 << BITRATE_SHIFT)); -$40 + cpy #31 + bcc :++; bit-rate $20 + dex; 17 +: and #.lobyte(~(%01 << BITRATE_SHIFT)); -$20 +: rts + +loaddone: sta CHECKSUM + +: lda (V1A),y; VIA1_PRA + bmi :- + + lax GCRBUFFER ; 11222223 + lsr ; .1122222:3 + lda (V2A),y; VIA2_PRA ; 3333LHLH + ror ; 33333LHL + lsr ; .33333LH:L + tay + lda DECGCRTABZZ43210ZHI,x; 2222.... + + SKIPWORD + + .assert * >= $02a9, error, "***** 1571 watchdog IRQ vector located below $02a9. *****" + .assert * <= $02a9, error, "***** 1571 watchdog IRQ vector located above $02a9. *****" + + .word uninstall + + ora DECGCRTABZ43210ZZLO,y; 22223333 + tay; ID0 + eor CHECKSUM + ;clc + rts; z = 1 + +chksumerr: ; with repeated checksum errors, head might have landed between tracks or on the wrong track + lda ERRORCOUNT; is changed to dec ERRORCOUNT after init + bne :+ + + ldx #ERT + stx ERRORCOUNT + lda (.lobyte(V2B - ERT),x); VIA2_PRB + anc #BITRATE | BUSY_LED | TRACK_STEP + sbc #(1 << BITRATE_SHIFT) - 1; cycle to the next bit-rate (denser until wrap) + adc #$03; and step down half a track on bit-rate wrap + ora #MOTOR + sta (.lobyte(V2B - ERT),x); VIA2_PRB + +: sec; operation not successful + rts; checksum mismatch: z = 0, c = 1 + + ; in: y: track + ; a: sector or negative value for any sector +getblkchid: ldx #.lobyte(compareid - (idswitch + 2)); check against stored ID +getblkstid: ; store read ID, ldx #.lobyte(storeid - (idswitch + 2)) executed by caller + ldy #DIRTRACK + stx idswitch + 1 +getblock71: sta REQUESTEDSECTOR + jsr trkseek71; stores the number of blocks on the current track in NUMSECTORS +getblkscan: +findblkhdr: lda #$ff + sta TIMER +: jsr waitsync + beq readerror; returns with carry set on time-out + cmp #%01010010; $52, check if the sync is followed by a block header + bne :- + + jsr readheader +bnecsumerr: bne chksumerr + + lda BLOCKBUFFER71 + $fd; sector + cmp NUMSECTORSONTRACK + bcs chksumerr + sta LOADEDSECTOR + + ldx BLOCKBUFFER71 + $fe; track + beq chksumerr + cpx MAXTRACK + bcs chksumerr + stx HEADERTRACK + + ldx BLOCKBUFFER71 + $ff; ID1 + + ; y = ID0 +idswitch: .byte OPC_RTS, 0; is changed to bcc storeid/compareid after init + +storeid: sty ID0 + stx ID1 +compareid: cpy ID0 + bne bnereaderr + cpx ID1 + bne bnereaderr + + ;lda LOADEDSECTOR + tax + eor REQUESTEDSECTOR + beq loadblock; branch if requested sector + bmi :+ + + ; specific sector requested but not reached yet + lda REQUESTEDSECTOR + cmp NUMSECTORSONTRACK + bcc findblkhdr + bcs readerror; jmp + +: ; negative value: no specific sector requested, out-of-order sector fetch + ;ldx LOADEDSECTOR + lda INDEXTABLE,x + ldy SECTORLINKTABLE71,x + ldx #255 - (BLOCKPENDING | INDEXSPECULATIVE) + sax CURRBLOCKINDEX + + asl + bcs :+; branch if block index not known nor speculated + ora #255 - (BLOCKPENDING * 2); with positive INDEXTABLE value, branch if the block has already been loaded into the host's memory (BLOCKPENDING not set) + tay +: iny; with negative INDEXTABLE value, block index is unknown: branch to findblkhdr if linked sector is known (the block has been loaded before already) + bne findblkhdr + + ; skip this block if the following block is next in the stream + ldx NEXTCONTIGUOUSBLOCK + bne :+ + ldx NUMSECTORSONTRACK +: dex + cpx LOADEDSECTOR + beq findblkhdr + +loadblock: ; wait for data block sync + jsr waitsync + cmp #%01010101; $55, check if the sync is followed by a data block + bne bnecsumerr + + lda #%11000000 + ;ldy #0 + jsr readdata + bne bnecsumerr + + lax HEADERTRACK + eor CURRTRACK71 + beq sanitychsw + + ; track error + jsr trkseek71c + asl CLEARSECTORLINKTABLE71 +bnereaderr: bne readerror; jmp + +linkerror: asl INDEXTABLE,x + bmi readerror; branch if INDEXSPECULATIVE is set: clear index, set MSB + ror INDEXTABLE,x; restore non-speculative index + SKIPWORD; skip to NUMFILES71 = clc +: sec ; refill directory buffer when disk has been changed + ror NUMFILES71 ; NUMFILES71 = $18 = OPC_CLC +readerror: ; ID mismatch or illegal track or sector (invalid track/sector link) +checkchg: ; must not change y + lax VIA2_PRB; check light sensor for disk removal + eor DISKCHANGEBUFFER + stx DISKCHANGEBUFFER + and #WRITE_PROTECT + bne :- + ; read error: z = 1 + sec; operation not successful + rts + + ; block link sanity check +sanitychsw: .byte OPC_RTS, LOADEDSECTOR; is changed to ldx LOADEDSECTOR after init + + lda #$fe + sta SECTORLINKTABLE71,x; mark block as loaded (linked sector is known) + + lda BLOCKBUFFER71 + SECTOROFFSET + sta LINKSECTOR71 + cmp #2 + + lda BLOCKBUFFER71 + TRACKOFFSET + sta LINKTRACK71 + sta TRACKLINKTABLE,x + beq chklastbsz + + cmp MAXTRACK; check whether track link is within the valid range + bcs linkerror; if not, return error + + jsr getnumscts; get number of sectors on linked track + cpx LINKSECTOR71; check whether sector link is within the valid range +chklastbsz: ; branch if invalid block size (0..1 = 1..2 bytes) + bcc linkerror; branch if sector number too large + + lda LINKSECTOR71 ; return the loaded block's sector link sector number + ldx LOADEDSECTOR ; return the loaded block's sector number + ldy LINKTRACK71 ; return the loader block's sector link track number + + cpy CURRTRACK71 + beq sametrack + asl ; must not be $ff + ora #%10000000; link is not to same track +sametrack: + cmp LOADEDSECTOR ; block must not link to itself + beq linkerror ; (but larger cycles are not detected) + sta SECTORLINKTABLE71,x ; set block link + cpy CURRTRACK71 ; z flag must be set + clc ; operation successful + rts + +waitsync: ldy #0 +: lda (TIM),y; TIMER + beq readerror; will return $00 in the accu + lda (V2B),y; VIA2_PRB, check SYNC + bmi :-; branch if no sync + lda (V2A),y; VIA2_PRA; clear latch +: lda (V1A),y; VIA1_PRA, check BYTEREADY + bmi :-; branch if byte not ready + lda (V2A),y; VIA2_PRA; is never $00 but usually $52 (header) or $55 (data) + rts + + ; get custom drive code +getcustom: ldx #0 + lda #ATNA_OUT | DATA_OUT; 1571 + sta (V1B,x); VIA1_PRB +: lda #CLK_IN + and (V1B,x); VIA1_PRB, wait for CLK_IN set + beq :- + lda #ATNA_OUT + sta (V1B,x); VIA1_PRB + +CUSTOMZPBUFFER71 = FILENAME71 +CUSTOMUPLOADSIZE71 = $bb + RETURNSIZE71 +RETURNSIZE71 = $22 + +: jsr getbytewdg + sta BLOCKBUFFER71,x + inx + cpx #CUSTOMUPLOADSIZE71 + bne :- + jmp BLOCKBUFFER71 + RETURNSIZE71 + +: inc CURRTRACK71 +uninstall: ldy #18; ROM dir track + jsr trkseek71 + ;ldy #0 + lda (V2B),y; VIA2_PRB + bit TWO; the stepper bits are set to %00 after reset: ensure full-track + bne :- ; stepper alignment (may still be off by a half-track) + + dec reset; OPC_ADC_ABS -> OPC_JMP_ABSI + + and #BUSY_LED + beq reset + dey + sty LEDSTATE71 + +statussent: bne fadeledidl + + ; prepare next file, only after successful load + ldx FILEDIRBUFFERINDEX + lda FILENAMEHASHVALSLO + 1,x + ldy FILENAMEHASHVALSHI + 1,x + jmp setnextfile + +fadeledidl: jsr fadeled + bne fadeledidl + +reset: adc RESET_VECTOR + +prpdnxtfil: ; jump from prpnxtfjmp when drive has cached next file's first block +idleloop71: jsr fadeled + jsr checkchg + ;lda #0 + tax + lda (V1B,x); VIA1_PRB + bpl uninstall + lsr + bcs idleloop71; wait until there is something to do + + jsr twomhz + + FILENAME = FILENAME71 + GET_FILENAME 1571 + + ; matches against hash of filename in FILENAMEHASHLO/HI + findfile71 = findfile + FIND_FILE 1571 + + ;ldy #diskio::status::FILE_NOT_FOUND; $ff +loadfile71: + bcs filenfound +DIRSECTORS71 = DIRSECTORS + ;lda DIRSECTORS,x + sta FILESECTOR + + lda #0 + sta PREVBLOCKINDEXPLUS1 + sta NUMCONTIGUOUSBLOCKS + +trackloop: sta BLOCKINDEXBASE + lda #BUSY_LED | MOTOR + jsr trackseekx + ; x contains the number of sectors on this track + inx +trackinit: sta INDEXTABLE - 2,x; accu is negative with INDEXSPECULATIVE set, sector indices are unknown + dex + bpl trackinit; sets REQUESTEDSECTOR = INDEXTABLE - 1 with x = 1 and FILETRACK = INDEXTABLE - 2 with x = 0 + + .assert .hibyte(*) = .hibyte(trackinit), error, "***** Page boundary crossing in trackinit loop. *****" + + stx LEDSTATE71 + ldx FILESECTOR + stx NEXTCONTIGUOUSBLOCK + ;ldy #0; initial block index + jsr speculinit; set non-speculative block indices according to known links, build initial speculative block index table for this track + + lda LOADEDSECTOR + cmp FILESECTOR + beq servecache + +blockloop: jsr getblkscan + bcs blockloop; branch if block fetch not successful + + ;ldx LOADEDSECTOR + ;ldy LINKTRACK71 + ;cpy CURRTRACK71 + bne nosetspcil; branch if linked block not on current track + sec + ;lda LINKSECTOR71 + sbc LOADEDSECTOR; determine likely interleave + tay + bcs setspecilv +nosetspcil: ldy SPECULATIVEINTERLEAVE +setspecilv:;ldx LOADEDSECTOR + lda INDEXTABLE,x + anc #.lobyte(~(BLOCKPENDING | INDEXSPECULATIVE)) +bmiblkloop: bmi blockloop; branch if block conceivably not belonging to the file + + sta CURRBLOCKINDEX + beq refuspecil; branch if first file block on track: cannot determine likely interleave + lda PREVBLOCKINDEXPLUS1; first file block must be loaded first in order to know the load address to be able to place successive + beq blockloop ; blocks in host memory, skip loaded blocks until the required one is read + + tya + beq refuspecil + cmp #MAXINTERLEAVE + 1; validate + bcs refuspecil ; speculative interleave + adc LINKSECTOR71 + cmp NUMSECTORSONTRACK + bcc :+ + sbc NUMSECTORSONTRACK +: ldx LINKSECTOR71 + cmp SECTORLINKTABLE71,x + bne refuspecil; only accept interleave if distances to this block and the next match + sty SPECULATIVEINTERLEAVE +refuspecil: + ldx MAXCONFIRMEDBLKIDXSEC + ldy MAXCONFIRMEDBLOCKINDEX + sec; clear mis-speculation detected flag + jsr specmanage +servecache: +prpnxtfjmp: jmp prpdnxtfil; is changed to eor prpdnxtfil + + jsr sendblock + bcs :+; branch if file closed + + bmi blockloop + bne trackloop + + ; loading is finished + +filenfound: tya; $00 = diskio::status::OK + + ldy #0; send over one byte + jsr sendstatus +: dec prpnxtfjmp; OPC_EOR_ABS -> OPC_JMP_ABS + lax BLOCKBUFFER71 + bcs fileclosed + + ldx #ATN_IN | ATNA_OUT | CLK_OUT | CLK_IN +: cpx VIA1_PRB; wait until host + beq :- ; is in idle mode + + tax + jmp statussent + +fileclosed:;ldy #$ff + iny + ;ldx BLOCKBUFFER71 + inx + beq :+; branch if file not found: DATA_OUT remains set, clear it otherwise + tya + sta (V1B),y; VIA1_PRB +: lda (V1B),y; VIA1_PRB, wait for ATN_IN set, + bpl :- ; acknowledgement of the block transfer + lda #ATNA_OUT | CLK_OUT; drive busy + sta (V1B),y; VIA1_PRB + txa + beq :+ + jmp idleloop71; ok +: jmp fadeledidl; file not found + +sendblock: ldx LOADEDSECTOR + lda INDEXTABLE,x + anc #.lobyte(~BLOCKPENDING); clc + sta INDEXTABLE,x + + ;clc + sbc #INDEXSPECULATIVE - 1 + ; c = 1 if index speculative, 0 otherwise + + bcc :+; branch if block index is not speculative + sta CURRBLOCKINDEX; speculative index might have changed + lda MAXCONFIRMEDBLOCKINDEX + cmp CURRBLOCKINDEX; do not transfer block if another block has been confirmed to have the same or a + bcc :+ ; higher index (this block's index is wrong or wouldn't be speculative any more) + + ;sec + ror INDEXTABLE,x; clear index, set MSB + clc + rts + +: lda NEXTCONTIGUOUSBLOCK +: tax + lda INDEXTABLE,x + cmp #MAXNUMSECTORS + bcs :++; branch if block has not been confirmed and transferred already + inc NUMCONTIGUOUSBLOCKS + lda SECTORLINKTABLE71,x + bpl :-; branch if there is a linked sector on the same track + + alr #%00011111 * 2 + sta FILESECTOR; first file sector on the linked track + ldy TRACKLINKTABLE,x + sty FILETRACK + beq nostep + + ; perform Shrydar Stepping (R)(TM) to minimise single-track stepping overhead: + ; nudge the R/W head in the right direction, then wait a few bycles while it gains momentum, + ; then enable the destination track's stepper magnet long before the head has reached the intermediate half-track's magnet, + ; relying on the head's inertia, then send over the block while the head keeps moving beyond the intermediate half-track's stepper magnet + ; and ultimately settles on the destination track. + ; sending the block over takes at least 72 bycles + ldy CURRTRACK71 + cpy FILETRACK + bcs :+ + iny + SKIPBYTE +: dey + ldx #$80 | SINGLESTEPSPEED + jsr trackstep +nostep: + ; calculate block index in host memory + sec + +: stx NEXTCONTIGUOUSBLOCK + + lda BLOCKINDEXBASE + adc CURRBLOCKINDEX + tay + sec + sbc PREVBLOCKINDEXPLUS1 + sty PREVBLOCKINDEXPLUS1 + ldy #0 + cpy LINKTRACK71 + rol; last block: set lsb, clear lsb otherwise + sta BLOCKBUFFER71 + $01; block index and last block size/next contiguous block index flag + + lsr + lda NUMCONTIGUOUSBLOCKS + dey + bcc :+ + ; handle file's last block + ;sec + lda #0 + sbc LINKSECTOR71 + ldy LINKSECTOR71; the file's last block's length (last byte index) +: ; accu: next contiguous block index/block size + +sendstatus: sta BLOCKBUFFER71; next contiguous block index/block size or status byte + sty BLOCKSIZE + + ldx #$20; here, the watchdog timer is polled manually because + ; an extra-long time-out period is needed since the computer may + ; still be busy decompressing a large chunk of data, + ; this is the round counter: $20 * ($ff00 - $0100) = 2,080,768 cycles at 2 MHz is roughly 1 second + ldy #$ff + sty TIMER; reset watchdog time-out + + lda #ATNA_OUT | CLK_OUT | DATA_OUT | DATA_IN; set DATA_OUT as well so there is a flank only on CLK when signalling ready, + sta (V1B - $20,x); VIA1_PRB, such that just one serial bus read can safely determine block ready (CLK clear) and drive present (DATA set) vs device not present (both clear) + lda #ATNA_OUT | DATA_OUT; clear CLK_OUT, set DATA_OUT as signal of presence + sta (V1B - $20,x); VIA1_PRB; block ready signal + + sec; for closefile + +waitready: lda TIMER; see if the watchdog barked + bne :+ + dex ; if yes, decrease the round counter +.if ::DISABLE_WATCHDOG + beq :+ +.else + beq timeout; and trigger watchdog on time-out +.endif + sty TIMER; reset watchdog time-out + +: lda #ATN_IN | CLK_IN + and VIA1_PRB + bmi waitready; wait for ATN_IN clear + + bne closefile + + sty TIMER; reset watchdog time-out + +timeout: ENABLE_WATCHDOG + iny; ldy #0 + +.if ::PLATFORM = diskio::platform::COMMODORE_128 + + lda #TWO_MHZ | FAST_SERIAL_OUTPUT + ora (V1A),y; VIA1_PRA + sta (V1A),y; VIA1_PRA + lda #IOMODE_OUTPUT | COUNT_PHI2 | CONTINUOUS | TIMER_START + sta CIA_CRA + lda #CLK_OUT; clear ATNA_OUT, $08 = SERIAL_IRQ + sta (V1B),y; VIA1_PRB + + bit CIA_ICR + + .if ::USE_ASYNCHRONOUS_BURST_HANDSHAKE + + ldx BLOCKBUFFER71,y +sendloop: cpy BLOCKSIZE + iny + stx CIA_SDR; clock out data byte + ldx #ATN_IN | DEVICE_NUMBER | ATNA_OUT; set ATNA_OUT, clear CLK_OUT +: bit CIA_ICR; wait until data sent + beq :- + stx TIMER; need some slack, as CIA sets the flag a little too early (MOS5710 doesn't) + stx VIA1_PRB; toggle CLK_OUT: signal data sent, toggle ATNA_OUT + ldx BLOCKBUFFER71,y +: bit VIA1_PRB; wait for ATN_IN set = data taken + bpl :- + bcs senddone + + cpy BLOCKSIZE + iny + stx CIA_SDR; clock out data byte + ldx #$ff +: bit CIA_ICR; wait until data sent + beq :- + stx TIMER; need some slack, as CIA sets the flag a little too early (MOS5710 doesn't) + sta VIA1_PRB; toggle CLK_OUT: signal data sent, toggle ATNA_OUT + ldx BLOCKBUFFER71,y +: bit VIA1_PRB; wait for ATN_IN clear = data taken + bmi :- + bcc sendloop +senddone: + + .else; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda BLOCKBUFFER71,y +sendloop: sta CIA_SDR; clock out data byte + cpy BLOCKSIZE + bcs waittaken + iny + lda BLOCKBUFFER71,y + ldx #ATN_IN | DEVICE_NUMBER | ATNA_OUT; set ATNA_OUT + stx TIMER +: bit VIA1_PRB; wait for ATN_IN set = data taken + bpl :- + stx VIA1_PRB; toggle ATNA_OUT + cpy BLOCKSIZE + sta CIA_SDR; clock out data byte + + bcs waitdtaken + iny + lda BLOCKBUFFER71,y + ldx #ATN_IN | DEVICE_NUMBER; clear ATNA_OUT + stx TIMER +: bit VIA1_PRB; wait for ATN_IN clear = data taken + bmi :- + stx VIA1_PRB; toggle ATNA_OUT + bcc sendloop; jmp + +waitdtaken: bit VIA1_PRB; wait for ATN_IN clear = data taken + bmi waitdtaken + bpl senddone; jmp +waittaken: bit VIA1_PRB; wait for ATN_IN set = data taken + bpl waittaken +senddone: + .endif; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE + +BURST_OFF = IOMODE_INPUT | COUNT_PHI2 | CONTINUOUS | TIMER_START + ldx #BURST_OFF + stx CIA_CRA + lda #.lobyte(~FAST_SERIAL_OUTPUT) + and (V1A - BURST_OFF,x); VIA1_PRA + sta (V1A - BURST_OFF,x); VIA1_PRA + + lda #ATNA_OUT | CLK_OUT; drive busy + sta (V1B - BURST_OFF,x); VIA1_PRB + +.else; ::PLATFORM <> diskio::platform::COMMODORE_128 + + ldx #255 - DATA_OUT +sendloop: lda BLOCKBUFFER71,y ; 4 + eor #%01110111 ; 2 + asl ; 2 + rol ; 2 + rol ; 2 + ; = 26 + +: bit VIA1_PRB ; 4 + bmi :- ; 3 + sax VIA1_PRB ; 4 + stx TIMER ; 4 - reset watchdog time-out + ror ; 2 + ror ; 2 + ; = 19 + +: bit VIA1_PRB ; 4 + bpl :- ; 3 + sax VIA1_PRB ; 4 + ror ; 2 + lsr ; 2 + ; = 15 + +: bit VIA1_PRB ; 4 + bmi :- ; 3 + sax VIA1_PRB ; 4 + lsr ; 2 + lsr ; 2 + cpy BLOCKSIZE ; 3 + iny ; 2 + ; = 20 + +: bit VIA1_PRB ; 4 + bpl :- ; 3 + sax VIA1_PRB ; 4 + bcc sendloop ; 3 + ; = 80 + + lda #ATNA_OUT | CLK_OUT; drive busy +: bit VIA1_PRB; wait for ATN low, + bmi :- ; acknowledgement of the last data byte + + sta VIA1_PRB + +.endif; ::PLATFORM <> diskio::platform::COMMODORE_128 + +: bit VIA1_PRB; wait for ATN_IN set, + bpl :- ; acknowledgement of the block transfer + + sei; disable watchdog + + lda NUMCONTIGUOUSBLOCKS + ldy FILETRACK + clc +closefile: rts + + ; validate block indices according to currently-known links +idxloop71: tax; link sector + iny; block index + tya + eor INDEXTABLE,x + bmi confirmidx; branch if index was neither known nor speculated until now + eor #BLOCKPENDING | INDEXSPECULATIVE + beq confirmidx; branch if not-yet-loaded speculative index matches + eor #BLOCKPENDING + beq confirmspc; branch if already-loaded speculative index matches +speculinit: clc ; mis-speculated block index detected +confirmidx: tya + ora #BLOCKPENDING +confirmspc: sta INDEXTABLE,x +specmanage: lda SECTORLINKTABLE71,x; linked sector + bpl idxloop71; branch if there is a linked block on the same track + + .assert .hibyte(*) = .hibyte(idxloop71), error, "***** Page boundary crossing in idxloop71. *****" + + stx MAXCONFIRMEDBLKIDXSEC + sty MAXCONFIRMEDBLOCKINDEX + + lda #255 - BLOCKPENDING + SKIPWORD +rspeculate: ldy SPECWRAPSECTORINDEX + stx SPECSTARTSECTOR + bcc :+; branch if mis-speculated block index detected + + ; on first track, respeculate from last speculative index when it now has a known link + lax SPECWRAPSECTOR + ora SECTORLINKTABLE71,x + bmi specabort + lda #255 - (BLOCKPENDING | INDEXSPECULATIVE) + cpy SPECWRAPSECTORINDEX + bcc rspeculate + clc +: + sta CLEARINDEXMASK; = SPECWRAPSECTOR + + ; mis-speculated block indices detected: rebuild speculative block index table + + ;clc; minus one block: do not transfer alleged final block, as it may be mis-speculated + ; and load beyond the actual file end address due to possibly being larger than the file's final block + ldx FILEDIRBUFFERINDEX + lda NUMBLOCKS,x + sbc BLOCKINDEXBASE + bcc specabort; limit to alleged number of file blocks + cmp NUMSECTORSONTRACK + ldx NUMSECTORSONTRACK + bcc :+ + txa +: sta GENSPECLIMIT + + sty CLEARIDXMIN; = SPECWRAPSECTORINDEX +clearidxtb: lda CLEARINDEXMASK + and INDEXTABLE - 1,x + cmp CLEARIDXMIN + beq :+ + bcc :+ + ror INDEXTABLE - 1,x +: dex + bne clearidxtb + + .assert .hibyte(*) = .hibyte(clearidxtb), error, "***** Page boundary crossing in clear index table loop. *****" + + ldx SPECSTARTSECTOR +speculate: iny + cpy GENSPECLIMIT + bcs specdone + stx SPECWRAPSECTOR + lda SECTORLINKTABLE71,x + bpl havenewidx; branch if there is a linked sector on the same track + txa + ;clc + adc SPECULATIVEINTERLEAVE +havenewidx: tax + sec + sbc NUMSECTORSONTRACK + bcs specwrap + lda INDEXTABLE,x + bpl specsectkn + tya + ora #BLOCKPENDING | INDEXSPECULATIVE + sta INDEXTABLE,x + sty SPECWRAPSECTORINDEX + bne speculate; jmp + + .assert .hibyte(*) = .hibyte(speculate), error, "***** Page boundary crossing in speculate loop. *****" + +specsectkn: inx + txa +specwrap: ldx BLOCKINDEXBASE; for the first file track, only speculate until wrap and stop on already-taken speculative sectors, + bne havenewidx ; as going on would likely lead to much mis-speculation and ultimately lower speed +specabort: rts + +specdone: ;sec + ror SPECWRAPSECTOR + rts + + .assert * <= BLOCKBUFFER71, error, "***** 1571 drive code too large. *****" + +dcodinit71: lda #ATNA_OUT | CLK_OUT; signal idle to the host with ATN_IN clear + sta VIA1_PRB + + ;ldx ROMOS_TRACK_DIFF + txa + ldx #.lobyte(topofstack71) + txs + pha + + lda #T1_FREE_RUNNING | PA_LATCHING_ENABLE; watchdog IRQ: count phi2 pulses, 16-bit free-running, + sta VIA2_ACR ; port a latching should not be needed here + ; (IC rather than discrete logic), + ; but it is enabled just to be sure + lda #READ_MODE | BYTE_SYNC_ENABLE + sta VIA2_PCR + + lda #MAXTRACK71 + 1 + sta MAXTRACK + lda #NUMTRACKS_A + sta MAXTRACK_A + +.if ::PLATFORM = diskio::platform::COMMODORE_128 + lda #5 ; default burst timing is 6, anything below 5 yields transfer + sta CIA_TA_LO; errors, hard-wired setting for 1571CR's MOS5710 is also 5 + ;lda #0 + ;sta CIA_TA_HI +.endif; ::PLATFORM = diskio::platform::COMMODORE_128 + + ; watchdog initialisation + lda #IRQ_CLEAR_FLAGS | IRQ_ALL_FLAGS + sta VIA1_IER; no IRQs from VIA 1 + sta VIA2_IER; no IRQs from VIA 2 + sta CIA_ICR; no IRQs from CIA + bit CIA_ICR + +TIMER_VAL = IRQ_SET_FLAGS | IRQ_TIMER_1; $c0 + ldx #TIMER_VAL +.if ::DISABLE_WATCHDOG + bit VIA2_IER; timer 1 IRQs from VIA 2 +.else + stx VIA2_IER; timer 1 IRQs from VIA 2 +.endif + ;ldx #$c0 + stx CLEARSECTORLINKTABLE71 + stx NUMFILES71 + + ; fade off the busy LED if lit + lda #.lobyte(~MOTOR) + and (.lobyte(VIA2_PRB - TIMER_VAL),x); VIA2_PRB + sta (.lobyte(VIA2_PRB - TIMER_VAL),x); VIA2_PRB + sta DISKCHANGEBUFFER; store light sensor state for disk removal detection + and #BUSY_LED + beq :+ + lda #$ff +: sta LEDSTATE71 + +: jsr fadeled + bne :- + + jsr twomhz + + lda #MAXNUMSECTORS + sta NUMSECTORSONTRACK + lda #INITIALSPECINTERLEAVE + sta SPECULATIVEINTERLEAVE + + ldy #$ff; invalid track number -> no step but turn on motor + pla; ROMOS_TRACK_DIFF + bne stepperok; branch if the drive had already seeked before the loader has been started + + ; the drive was reset immediately before running the loader - + ; step down a track: this works normally if the stepping bits are congruent with the stepper motor; + ; however, it may happen that the bits are misaligned (opposite to the actual stepper position, bit 1 + ; reversed), this alone does not move the head but stepping makes it go into the direction opposite to + ; the one desired when moving; the stepping down two halftracks will actually step up and step down one + ; halftrack each and thus will end up on the same track as before, but align the stepper bits to the motor. + + ldy #2 + sty CURRTRACK71 + dey +stepperok: jsr trkseek71 + + ldx #findtrkned - findtrkerr +: lda findtrkerr - 1,x + sta FINDTRACKN - 1,x + dex + bne :- + + .assert (FINDTRACKN + findtrkned - findtrkerr) <= enddirbuffer, error, "***** 1571 findtrackno too large. *****" + + stx REQUESTEDSECTOR + jmp findtrckno - findtrkerr + FINDTRACKN + +findtrkerr: dec bitratecnt + bne findtrknum + lax VIA2_PRB + axs #$0100 - (1 << BITRATE_SHIFT) + stx VIA2_PRB; cycle through the 4 bit-rates + + dec ERRORCOUNT + bne :+ + lda #OPC_RTS + sta seekswitch + jsr halftrkdwn + jsr twomhz + lda #OPC_TAX + sta seekswitch +findtrckno: lda #ERR + sta ERRORCOUNT + +: lda #BITRATECOUNT + sta bitratecnt + + ; find current track number +findtrknum: lda #SUCCESSCOUNT + sta successcnt + +: jsr getblkscan; any sector, no block link sanity check, store ID + bcs findtrkerr + ldx HEADERTRACK + cpx CURRTRACK71 + stx CURRTRACK71 + bne findtrknum; on different track number, retry with same bit-rate + jsr loadblock + bcs findtrkerr + dec successcnt; accept current track number only after some consecutive successful attempts + bne :- + + lda #OPC_DEC_ZP + sta chksumerr + lda #OPC_BCC + sta idswitch + lda #OPC_LDX_ZP + sta sanitychsw + + jsr initlinktb + + ldy #DIRTRACK + jsr trkseek71 + jmp idleloop71 +findtrkned: +drvcodeend71: + +TRAMPOLINEOFFSET = $24; dgetrout - dinstall + 1 + + .org * - TRAMPOLINEOFFSET + +dinstall: sei + ldx ROMOS_TRACK_DIFF; $42 + txs + + lda #.lobyte(~TWO_MHZ); transfer in slow mode in order to be + and VIA1_PRA ; less susceptible to interference by + sta VIA1_PRA ; any passive drives running at 1 MHz + + lda #ATNA_OUT | CLK_OUT + sta VIA1_PRB + lda #VIA_ATN_IN_INPUT | VIA_DEVICE_NUMBER_OUTPUT | VIA_ATNA_OUT_OUTPUT | VIA_CLK_OUT_OUTPUT | VIA_CLK_IN_INPUT | VIA_DATA_OUT_OUTPUT | VIA_DATA_IN_INPUT + sta VIA1_DDRB +: lda VIA1_PRB; wait for ATN_IN set and DATA_IN clear + lsr + bcs :- + lda #ATNA_OUT + sta VIA1_PRB + + ldx #.lobyte(drvcodebeg71 - 1) +dgetrout: inx + + .assert * >= drvcodeend71, error, "***** 1571 trampoline too low in memory. *****" + + bne :+ + inc dgetputhi +: DRIVEGETBYTE 1571, getbyteinstall; there is no watchdog while installing +dgetputhi = * + 2 + sta a:.hibyte(drvcodebeg71 - 1) << 8,x + cpx #.lobyte(drvcodeend71 - 1) + bne dgetrout + dec drvcodebeg71 + bne dgetrout + + tsx; ROMOS_TRACK_DIFF + jmp dcodinit71 + +drvprgend71: + .assert * <= $0800, error, "***** 1571 drive code too large. *****" + + .reloc diff --git a/loader/src/drives/drivecode1581.s b/loader/src/drives/drivecode1581.s new file mode 100755 index 0000000..d0c9f02 --- /dev/null +++ b/loader/src/drives/drivecode1581.s @@ -0,0 +1,903 @@ + +.include "cpu.inc" +.include "cia.inc" +.include "via.inc" + +.include "drives/drivecode-common.inc" + +ZP_FIRST = $00 + +BUFFER = $00 +SYS_SP = $01 +JOBCODESTABLE = $02; fixed in ROM +JOBTRKSCTTABLE = $0b; fixed in ROM - $0b..$1c +FILETRACK = $0b +FILESECTOR = $0c +FILENAMEHASHLO = $0d +FILENAMEHASHHI = $0e +NUMFILES = $0f +CURRDIRBLOCKTRACK = $10 +CYCLESTARTENDTRACK = $11 +CYCLESTARTENDSECTOR = $12 +NEXTDIRBLOCKTRACK = $13 +NEXTDIRBLOCKSECTOR = $14 +FIRSTDIRSECTOR = $15 +NEXTCONTIGUOUSBLOCK = $16 +TEMP = $17 + +ZP_LAST = $17 + +;BLOCKBUFFERJOBTRACK = $1b; fixed in ROM - track for job at buffer 8 ($0b00) +;BLOCKBUFFERJOBSECTOR = $1c; fixed in ROM - sector for job at buffer 8 ($0b00) + +DISKCHANGED = $25; fixed in ROM +DRIVESTATE = $26; fixed in ROM +DRIVEOFF = $00; literal +OPEN_FILE_TRACK = $4c; fixed in ROM + +LEDSTATE = $79; fixed in ROM + +IRQVECTOR_LO = $0192 +IRQVECTOR_HI = $0193 +HDRS2 = $01bc +ROMOS_DIRTRACK81 = $022b +OPEN_FILE_SECTOR = $028b + +STROBE_CONTROLLER = $ff54 + +OK_DV = $00 +READ_DV = $80 +MOTOFFI_DV = $8a +SEEK_DV = $8c + +RESET_TIMERB = $cb9f +CONTROLLERIRQPERIODFD = $4e20 + +BUFFER0 = $0300 +BUFFERSIZE = $0100 + +SENDTABLELO = $0900 +SENDTABLEHI = $0a00 + +BLOCKBUFFER81 = $0b00 +TRACKOFFSET = 0 +SECTOROFFSET = 1 + +REQUESTEDTRACK = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + TRACKOFFSET +REQUESTEDSECTOR = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + SECTOROFFSET +LOADEDSECTOR = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + SECTOROFFSET + +LINKTRACK = BLOCKBUFFER81 + TRACKOFFSET +LINKSECTOR = BLOCKBUFFER81 + SECTOROFFSET + +BINARY_NIBBLE_MASK = %00001111 + +ROMOS_MAXTRACK = $8f; MAXTRACK81 - 1 +ROMOS_MAXSECTOR = $75; MAXSECTOR81 + 1 +MAXTRACK81 = 80; literal +MAXSECTOR81 = 39; literal + +BUFFERINDEX = (BLOCKBUFFER81 - BUFFER0) / BUFFERSIZE + + .org $0300 + +.export cmdfdfix0 : absolute +.export cmdfdfix1 : absolute +.export cmdfdfix2 : absolute +.if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!::DISABLE_WATCHDOG) +.export cmdfdfix3 : absolute +.export cmdfdfix4 : absolute +.endif + +drvcodebeg81: .byte .hibyte(drvcodeend81 - * + $0100 - $01); init transfer count hi-byte + +sysirqvbuf: .word 0 + +syszpbuf: .res ZP_LAST - ZP_FIRST + 1 + +filename81: .res 16 + + .res 24; overflow area for 1541 custom code upload trampoline + +swapzp81: ldx #ZP_LAST - ZP_FIRST +: lda ZP_FIRST,x + ldy syszpbuf,x + sty ZP_FIRST,x + sta syszpbuf,x + dex + bpl :- + rts + + FNAMEHASH 1581 + +getdirtrk: +cmdfdfix1 = * + 1 +cmdfdfix2 = * + 2 + lda ROMOS_DIRTRACK81 +.if DIRTRACK81 = 40 + rts +.endif + cmp #40 + bne :+ + lda #DIRTRACK81 +: +.if DIRTRACK81 <> 40 + rts +.endif + +trackseek: sta REQUESTEDTRACK + sty REQUESTEDSECTOR + tax + dex + stx HDRS2 + (2 * BUFFERINDEX) + jsr initcntr81 + lda #SEEK_DV + ldx #BUFFERINDEX + jsr STROBE_CONTROLLER + + lda DISKCHANGED + ora diskchangd + 1 + sta diskchangd + 1 + + ; fall through + +initwdog81: ; the i-flag is set here +.if ::DISABLE_WATCHDOG + rts +.endif + lda #.lobyte(uninstall) + sta IRQVECTOR_LO + lda #.hibyte(uninstall) + sta IRQVECTOR_HI + lda cmdfdfix2; 0 for FD + beq :+ + lda #$ff + sta CIA_TB_LO + sta CIA_TB_HI +: +.if !::DISABLE_WATCHDOG + rts +.endif + +enablwdg81: lda cmdfdfix2; 0 for FD + beq :+ + ldy #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sty CIA_CRB + bit CIA_ICR + rts +: lda #IRQ_CLEAR_FLAGS | IRQ_ALL_FLAGS + sta VIA_IER; no IRQs from VIA + lda #IRQ_SET_FLAGS | IRQ_TIMER_1 + sta VIA_IER; timer 1 IRQs from VIA + ldy #$ff + sty VIA_T1C_H + rts + +getblock81: sta REQUESTEDTRACK + sty REQUESTEDSECTOR +getblockag: jsr initcntr81 + lda #READ_DV + ldx #BUFFERINDEX + jsr STROBE_CONTROLLER + + lda DISKCHANGED + ora diskchangd + 1 + sta diskchangd + 1 + + jsr initwdog81 + + lda JOBCODESTABLE + BUFFERINDEX; FD does not return the error status in the accu + cmp #OK_DV + 1 + + ; the link track is returned last so that the z-flag + ; is set if this block is the file's last one (see FIND_FILE) + ldy LINKSECTOR + ldx LOADEDSECTOR + lda LINKTRACK + rts + +initcntr81: lda sysirqvbuf + sta IRQVECTOR_LO + lda sysirqvbuf + 1 + sta IRQVECTOR_HI + lda cmdfdfix2; 0 for FD + beq :+ + jmp RESET_TIMERB +: lda #.lobyte(CONTROLLERIRQPERIODFD) + sta VIA_T1C_L + lda #.hibyte(CONTROLLERIRQPERIODFD) + sta VIA_T1C_H + rts + +fadeled: txa + tay + beq motrledoff +: nop + bit OPC_BIT_ZP + dey + bne :- + pha + jsr busyledoff + pla + tay +: nop + bit OPC_BIT_ZP + iny + bne :- + dex +bsyledon81: lda #DRIVE_LED + ora CIA_PRA + ldy #$ff + bne store_cia; jmp + +motrledoff: lda DRIVESTATE + ora LEDSTATE + beq :+ + + txa + pha + + ; fill track cache + lda REQUESTEDTRACK + ldy REQUESTEDSECTOR + jsr getblock81 + + ; turn off motor + jsr initcntr81 + lda #MOTOFFI_DV + ldx #BUFFERINDEX + jsr STROBE_CONTROLLER + lda #DRIVEOFF; $00 + sta DRIVESTATE + + pla + tax + +busyledoff: lda CIA_PRA + and #.lobyte(~DRIVE_LED); turn off drive led + ldy #$00 +store_cia: sta CIA_PRA + sty LEDSTATE +: rts + +uninstall: jsr getdirtrk + jsr trackseek + + ldx LEDSTATE +: jsr fadeled + lda LEDSTATE + bne :- + + ldx SYS_SP + txs + ldx #ZP_LAST - ZP_FIRST +: lda syszpbuf,x + sta ZP_FIRST,x + dex + bpl :- + jsr initcntr81 + + lda #1 + sta DISKCHANGED; re-read BAM etc. + + lda #CIA_SET_INTF | FLAG1_IRQ | SERIAL_IRQ | TIMERB_IRQ + sta CIA_ICR + bit CIA_ICR + cli + rts + +idleloop: ldx #$00; turn off motor and busy led + lda #DRIVE_LED; check if busy led is lit + and CIA_PRA + beq driveidle + dex; fade off the busy led, then turn off motor +driveidle: jsr fadeled; fade off the busy led + lda CIA_PRB + and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN + eor #ATN_IN | CLK_OUT | CLK_IN | DATA_IN + beq driveidle; wait until there is something to do + lda CIA_PRB; to be safe, read a second time + and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN + eor #ATN_IN | CLK_OUT | CLK_IN | DATA_IN + lsr; check for reset, uninstallation or custom drive code upload + beq :+ + jmp uninstall; check for reset or uninstallation + +: jsr enablwdg81; enable watchdog, the computer might be reset while sending over a + ; byte, leaving the drive waiting for handshake pulses + ENABLE_WATCHDOG + + filename = filename81 + GET_FILENAME 1581 + + ; matches against hash of filename in FILENAMEHASHLO/HI + BLOCKBUFFER = BLOCKBUFFER81 + findfile81 = findfile + FIND_FILE 1581 +loadfile81: + bcs filenfound + + lda FILENAMEHASHVALSLO + 1,x ; for load-next + sta FILENAMEHASHLO ; functionality, + lda FILENAMEHASHVALSHI + 1,x ; store hash of + sta FILENAMEHASHHI ; next file's name + lda DIRTRACKS,x + tax + + ; check for illegal track or sector + beq toillegal + cpx ROMOS_MAXTRACK; #MAXTRACK81 - 1 + beq :+ + bcs toillegal + 1 +: cpy ROMOS_MAXSECTOR; #MAXSECTOR81 + 1 + bcc :+ +toillegal: sec +cmdfdfix0: jmp illegalts; is changed to bit illegalts on FD2000/4000 to disable illegal track or sector error, + ; ROM variables for logical track/sector boundaries aren't known (probably around MAXTRACKFD = $54) + +: tya + pha + jsr bsyledon81 + pla + tay; FILESECTOR + txa; FILETRACK + + ldx #1 + stx NEXTCONTIGUOUSBLOCK + +loadblock: sta REQUESTEDTRACK + sty REQUESTEDSECTOR +: jsr getblockag + bcs :- + ;ldy LINKSECTOR + ;lda LINKTRACK + pha + beq :+ + ldy #$ff +: lda #1 + ldx #0 + cpx LINKTRACK + rol; last block: set lsb, clear lsb otherwise + tax + lsr + lda LINKSECTOR; the file's last block's length (last byte index) + pha + sty blocksize + 1 + dey + tya + eor #$ff + bcs :+ + lda NEXTCONTIGUOUSBLOCK +: stx BLOCKBUFFER81 + 1; block index and last block size/next contiguous block index flag + ; accu: next contiguous block index/block size + jsr sendblock; send the block over + inc NEXTCONTIGUOUSBLOCK + pla; LINKSECTOR + tay + pla; LINKTRACK + bcs fileclosed + bne loadblock + + ; loading is finished + + clc; all ok after loading + +filenfound: ; branches here with carry set +illegalts: ; or illegal t or s + + jsr sendstatus + bcs fileclosed + +: lda CIA_PRB + and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN + cmp #ATN_IN | CLK_OUT | CLK_IN | DATA_IN + bne :-; wait until host is in idle mode + + lda BLOCKBUFFER81; offset 0: status byte + bmi :+; only after successful load + + jmp prpnxtfile; jumps to prpdnxtfil +prpdnxtfil: ; always succeeds, trackseek to track in y is valid +trseekidle: jsr trackseek +: jmp idleloop + +fileclosed: inc BLOCKBUFFER81 + beq :+; branch if file not found: DATA_OUT remains set, clear it otherwise + lda #0 + sta CIA_PRB +: bit CIA_PRB; wait for ATN_IN set, + bpl :- ; acknowledgement of the block transfer + lda #CLK_OUT; drive busy + sta CIA_PRB + jmp idleloop + +inittimers: lda cmdfdfix2; 0 for FD + beq :+ +.if ::PLATFORM = diskio::platform::COMMODORE_128 + lda #5; set burst timer, anything below 5 yields transfer errors + sta CIA_TA_LO + lda #0 + sta CIA_TA_HI + lda #IOMODE_OUTPUT | COUNT_PHI2 | FORCE_LOAD | CONTINUOUS | TIMER_START; enable burst mode + sta CIA_CRA +.endif; ::PLATFORM = diskio::platform::COMMODORE_128 +.if !::DISABLE_WATCHDOG + ; watchdog initialisation + jsr initwdog81 + lda #CIA_CLR_INTF | EVERY_IRQ + sta CIA_ICR + lda #CIA_SET_INTF | TIMERB_IRQ + sta CIA_ICR + bne :++; jmp +: jsr initwdog81 +.endif +: rts + + ; get custom drive code +getcustom: lda #CLK_OUT; 1581 + sta CIA_PRB + lda #CLK_IN +: bit CIA_PRB; wait for CLK_IN set + beq :- + + ldx SYS_SP + txs + jsr swapzp81; restore system zeropage values + jsr initcntr81 + lda #CIA_SET_INTF | FLAG1_IRQ | SERIAL_IRQ | TIMERB_IRQ + sta CIA_ICR + bit CIA_ICR + +customparm = TEMP +uploadrout = $2000 - customend + getbyte; as high up in RAM as possible +CUSTOMPARAM81 = customparm +CUSTOMRECEIVE81 = uploadrout + + sei + ldx #customend - getbyte +: lda getbyte - 1,x + sta uploadrout - 1,x + dex + bne :- + lda #OPC_STA_ZPXI + sta uploadrout + getbyterts - getbyte + + ;ldx #0 + stx CIA_PRB; clear DATA_OUT + + ldx #6 +: jsr getbyte + sta customparm - 1,x + dex + bne :- + jmp uploadrout + + ; must not clobber x +getbyte: DRIVEGETBYTE 1581 +getbyterts: rts; is changed to sta (zp,x) for custom drive code upload + .byte customparm + 2 + inc customparm + 2 + bne :+ + inc customparm + 3 +: inc customparm + 4 + bne getbyte + inc customparm + 5 + bmi getbyte + jmp (customparm); execute custom drive code +customend: + + ; carry: clear = ok, set = load error +sendstatus: lda #0 + sta blocksize + 1 + + lda #diskio::status::FILE_NOT_FOUND + bcs sendblock + lda #diskio::status::OK +sendblock: sta BLOCKBUFFER81; next contiguous block index/block size + + jsr enablwdg81 + + lda #CLK_OUT | DATA_OUT; set DATA_OUT as well so there is a flank only on CLK when signalling ready, + sta CIA_PRB; such that just one serial bus read can safely determine block ready (CLK clear) and drive present (DATA set) vs device not present (both clear) + lda #DATA_OUT; clear CLK_OUT, set DATA_OUT as signal of presence + sta CIA_PRB; block ready signal + + ldx #$20; here, the watchdog timer is polled manually because + ; an extra-long time-out period is needed since the computer may + ; still be busy decompressing a large chunk of data, + ; this is the round counter: $20 * ($ff00 - $0100) = 2,080,768 cycles at 2 MHz is roughly 1 second + lda cmdfdfix2; 0 for FD + bne waitready + +waitrdyfd: lda VIA_T1C_H; see if the watchdog barked + bne :+ + dex ; if yes, decrease the round counter +.if ::DISABLE_WATCHDOG + beq :+ +.else + beq timeout; and trigger watchdog on time-out + ;ldy #$ff + sty VIA_T1C_H; reset watchdog time-out +.endif +: lda #CLK_IN + bit CIA_PRB + bmi waitrdyfd; wait for ATN_IN clear +.if !::DISABLE_WATCHDOG + sty VIA_T1C_H; reset watchdog time-out +.endif + jmp :++ + +closefile: sec + rts + +waitready: lda CIA_TB_HI; see if the watchdog barked + bne :+ + dex ; if yes, decrease the round counter +.if ::DISABLE_WATCHDOG + beq :+ +.else + beq timeout; and trigger watchdog on time-out + ;ldy #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sty CIA_CRB; reset watchdog time-out +.endif +: lda #CLK_IN + bit CIA_PRB + bmi waitready; wait for ATN_IN clear +.if !::DISABLE_WATCHDOG + sty CIA_CRB; reset watchdog time-out +.endif +: bne closefile +timeout: ENABLE_WATCHDOG + + ldy #$00 + +.if ::PLATFORM = diskio::platform::COMMODORE_128 + + bit CIA_ICR + lda cmdfdfix2; 0 for FD + beq fdsendblk + ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT + stx CIA_PRB + + .if ::USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda BLOCKBUFFER81,y +sendloop: +blocksize: cpy #$00 + iny + sta CIA_SDR; clock out data byte + lda #SERIAL_IRQ + ldx #FSM_BUS_DRIVER_OUTPUT; clear CLK_OUT +: bit CIA_ICR; wait until data sent + beq :- + .if !::DISABLE_WATCHDOG + lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sta CIA_CRB; reset watchdog time-out + .else + nop; need some slack, as CIA sets the flag a little too early + nop + .endif + stx CIA_PRB; toggle CLK_OUT: signal data sent + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN set = data taken + bpl :- + bcs bcssendone + + cpy blocksize + 1 + iny + sta CIA_SDR; clock out data byte + lda #SERIAL_IRQ + ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT +: bit CIA_ICR; wait until data sent + beq :- + .if !::DISABLE_WATCHDOG + lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sta CIA_CRB; reset watchdog time-out + .else + nop; need some slack, as CIA sets the flag a little too early + nop + .endif + stx CIA_PRB; toggle CLK_OUT: signal data sent + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN clear = data taken + bmi :- + bcc sendloop +bcssendone: bcs senddone; jmp + +fdsendblk: lda #$38 + sta VIA_PRA; set to output + lda VIA_ACR + and #.lobyte(~SHIFT_REG_CONTROL) + ora #SHIFT_OUT_T2 + sta VIA_ACR + + lda BLOCKBUFFER81,y +fdsendloop: sta VIA_T2C_H + eor #$ff + sta VIA_SR; clock out data byte + cpy blocksize + 1 + iny + lda #IRQ_SHIFT_REG + ldx #FSM_BUS_DRIVER_OUTPUT; clear CLK_OUT +: bit CIA_ICR; wait until data sent + beq :- + .if !::DISABLE_WATCHDOG + lda #$ff + sta VIA_T1C_H; reset watchdog time-out + .else + nop; need some slack + nop + .endif + stx CIA_PRB; toggle CLK_OUT: signal data sent + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN set = data taken + bpl :- + bcs senddone + sta VIA_T2C_H + eor #$ff + sta VIA_SR; clock out data byte + cpy blocksize + 1 + iny + lda #IRQ_SHIFT_REG + ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT +: bit CIA_ICR; wait until data sent + beq :- + .if !::DISABLE_WATCHDOG + lda #$ff + sta VIA_T1C_H; reset watchdog time-out + .else + nop; need some slack, as CIA sets the flag a little too early + nop + .endif + stx CIA_PRB; toggle CLK_OUT: signal data sent + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN clear = data taken + bmi :- + bcc fdsendloop + + .else ; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda BLOCKBUFFER81,y +sendloop: sta CIA_SDR; clock out data byte +blocksize: cpy #$00 + bcs bcswaittkn + iny + .if !::DISABLE_WATCHDOG + lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sta CIA_CRB; reset watchdog time-out + .endif +: bit CIA_PRB; wait for ATN_IN set = data taken + bpl :- + cpy blocksize + 1 + lda BLOCKBUFFER81,y + sta CIA_SDR; clock out data byte + + bcs waitdtaken + iny + .if !::DISABLE_WATCHDOG + lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sta CIA_CRB; reset watchdog time-out + .endif +: bit CIA_PRB; wait for ATN_IN clear = data taken + bmi :- + lda BLOCKBUFFER81,y + bcc sendloop; jmp + +fdsendblk: lda #$38 + sta VIA_PRA; set to output + lda VIA_ACR + and #.lobyte(~SHIFT_REG_CONTROL) + ora #SHIFT_OUT_T2 + sta VIA_ACR + + lda BLOCKBUFFER81,y + sta VIA_T2C_H + eor #$ff + sta VIA_SR; clock out data byte +fdsendloop: cpy blocksize + 1 +bcswaittkn: bcs waittaken + iny + .if !::DISABLE_WATCHDOG + lda #$ff + sta VIA_T1C_H; reset watchdog time-out + .endif + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN set = data taken + bpl :- + sta VIA_T2C_H + eor #$ff + sta VIA_SR; clock out data byte + + cpy blocksize + 1 + bcs waitdtaken + iny + .if !::DISABLE_WATCHDOG + lda #$ff + sta VIA_T1C_H; reset watchdog time-out + .endif + lda BLOCKBUFFER81,y +: bit CIA_PRB; wait for ATN_IN clear = data taken + bmi :- + sta VIA_T2C_H + eor #$ff + sta VIA_SR; clock out data byte + bcc fdsendloop; jmp + +waitdtaken: bit CIA_PRB; wait for ATN_IN clear = data taken + bmi waitdtaken + bpl senddone; jmp +waittaken: bit CIA_PRB; wait for ATN_IN set = data taken + bpl waittaken + + .endif; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE + +senddone: lda cmdfdfix2; 0 for FD + bne :+ + lda #$18 + sta VIA_PRA +: +.else; ::PLATFORM <> diskio::platform::COMMODORE_128 + +sendloop: + .if !::DISABLE_WATCHDOG +cmdfdfix3 = * + 1 + lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START ; is changed to VIA access for FD +cmdfdfix4 = * + 1 + sta CIA_CRB ; 2 + 4 - reset watchdog time-out + .endif + ldx BLOCKBUFFER81,y ; 4 + lda SENDTABLELO,x ; 4 + ; = 22 (+6 with watchdog) + +: bit CIA_PRB ; 4 + bmi :- ; 3 + sta CIA_PRB ; 4 + asl ; 2 + and #.lobyte(~ATNA_ENABLE_OUT) ; 2 + ; = 15 + +: bit CIA_PRB ; 4 + bpl :- ; 3 + sta CIA_PRB ; 4 + lda SENDTABLEHI,x ; 4 + ; = 15 + +: bit CIA_PRB ; 4 + bmi :- ; 3 + sta CIA_PRB ; 4 + asl ; 2 + and #.lobyte(~ATNA_ENABLE_OUT) ; 2 +blocksize: cpy #0 ; 2 + iny ; 2 + ; = 19 + +: bit CIA_PRB ; 4 + bpl :- ; 3 + sta CIA_PRB ; 4 + bcc sendloop ; 3 + ; = 75 +: bit CIA_PRB; wait for acknowledgement + bmi :- ; of the last data byte + +.endif; ::PLATFORM <> diskio::platform::COMMODORE_128 + + lda #CLK_OUT; drive busy + sta CIA_PRB + +: bit CIA_PRB; wait for ATN_IN set, + bpl :- ; acknowledgement of the block transfer + + sei; disable watchdog + clc + rts + +dcodinit81: lda #CLK_OUT + sta CIA_PRB; signal idle to the host, note that ATNA response with ATN_IN low is not possible on 1581, + ; so a KERNAL LISTEN command cannot be detected for automatic uninstallation + jsr swapzp81 + tsx + stx SYS_SP + + lda IRQVECTOR_LO + sta sysirqvbuf + 0 + lda IRQVECTOR_HI + sta sysirqvbuf + 1 + + ldx #$00 +: txa + sta TEMP + lsr + lsr + alr #%00100010 + eor TEMP ; bit 3 ^ bit 0 + and #%00010001; if result is 0: both bits are equal (no swap required) + ;clc ; if result is 1: swap bits by inverting them + adc #%01110111; 0 -> 7, 1 -> 8 + ora #%01100110; 0 -> 7, 1 -> e + eor #%10001000; 0 -> f, 1 -> 6 + eor TEMP + tay + and #BINARY_NIBBLE_MASK + sta SENDTABLELO,x + tya + lsr + lsr + lsr + lsr + tay + sta SENDTABLEHI,x + inx + bne :- + + jsr inittimers + + lda NUMFILES + bne :+ + + jsr getdirtrk + ldy #0 + jsr trackseek + lda #$ff + sta NUMFILES +: jmp idleloop + +drvcodeend81: +DRVCODEND81 = * +.export DRVCODEND81 + +DIRBUFFSIZE = (SENDTABLELO - *) / 4 +DIRTRACKS = * +DIRSECTORS = DIRTRACKS + DIRBUFFSIZE +FILENAMEHASHVALSLO = DIRSECTORS + DIRBUFFSIZE +FILENAMEHASHVALSHI = FILENAMEHASHVALSLO + DIRBUFFSIZE + +DIRTRACKS81 = DIRTRACKS + + .assert DIRBUFFSIZE >= 9, error, "***** Dir buffer too small. *****" + +DIRBUFFSIZE81 = DIRBUFFSIZE +.export DIRBUFFSIZE81 + + .assert * <= BLOCKBUFFER81, error, "***** 1581 drive code too large. *****" + +dinstall: sei + lda #CIA_ATN_IN_INPUT | WRITE_PROTECT_OUTPUT | FSM_BUS_DRIVER_DIRECTION_OUTPUT | ATNA_ENABLE_OUT_OUTPUT | CLK_OUT_OUTPUT | CLK_IN_INPUT | DATA_OUT_OUTPUT | DATA_IN_INPUT + sta CIA_DDRB + lda #CLK_OUT + sta CIA_PRB + + ldx #.lobyte(drvcodebeg81 - $01) + +: lda CIA_PRB; wait for ATN_IN set and DATA_IN clear + and #ATN_IN | DATA_IN + cmp #ATN_IN + bne :- + +dgetrout: inx + bne :+ + inc dgetputhi +: + ; must not clobber x + lda #%10000000 + sta BUFFER + sta CIA_PRB +: lda CIA_PRB +: cmp CIA_PRB + beq :- + lda CIA_PRB + and #CLK_IN + cmp #CLK_IN + ror BUFFER + bcc :-- + lda BUFFER +dgetputhi = * + $02 + sta a:.hibyte(drvcodebeg81 - $01) << 8,x + cpx #.lobyte(drvcodeend81 - $01) + bne dgetrout + dec drvcodebeg81 + bne dgetrout + + jmp dcodinit81 + +drvprgend81: + .reloc diff --git a/loader/src/hal/hal-c16.inc b/loader/src/hal/hal-c16.inc new file mode 100644 index 0000000..e7af7f9 --- /dev/null +++ b/loader/src/hal/hal-c16.inc @@ -0,0 +1,363 @@ + +.ifndef _HAL_C16_INC_ +_HAL_C16_INC_ = 1 + +.include "cpu.inc" +.include "pio.inc" +.include "ted.inc" + + +.macro CHECK_INSTALL_END_ADDRESS + .assert * <= $8000, error, "Install code exceeds $8000, please make sure the DISKIO_INSTALL segment ends below $8000" +.endmacro + +.if LOAD_VIA_KERNAL_FALLBACK + .macro CHECK_RESIDENT_START_ADDRESS + RESIDENT_START_ADDRESS = * + .endmacro + + .macro CHECK_RESIDENT_END_ADDRESS + .assert * <= $8000, error, "Resident code exceeds $8000, please make sure the DISKIO segment ends below $8000" + .endmacro +.else + .macro CHECK_RESIDENT_START_ADDRESS + .assert * <= $fd00, error, "Resident code exceeds $fd00, please make sure the DISKIO segment ends below $fd00" + .endmacro + + .macro CHECK_RESIDENT_END_ADDRESS + .assert * <= $fd00, error, "Resident code exceeds $fd00, please make sure the DISKIO segment ends below $fd00" + .endmacro +.endif + + +.macro OK_CLC + lda #diskio::status::OK; $00 + clc; all ok +.endmacro + +.macro PREPARE_DRIVE_DISTURBANCE_VALIDATION + ; disregard drive if it is a 1551, as + ; it is not connected to the serial bus + lda #$01 + sta USE4DY +.endmacro + +.macro BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS to + lda USE4DY; calling LISTEN will set USE4DY to $00 if 1551 at #9, + eor #$01 ; $30 if 1551 at #8, and leave it at $01 otherwise, + bne to ; then drvlistn will return with $80, $b0, or $01 +.endmacro + + +.if LOAD_VIA_KERNAL_FALLBACK + + .macro BUFFER_MEMCONFIG + GET_MEMCONFIG + pha + .endmacro + + .macro RESTORE_MEMCONFIG_Y + tay + pla + SET_MEMCONFIG + tya + .endmacro + + .macro PREPARE_PARALLEL_CHECK + lda #$01 ; calling LISTEN will set USE4DY to $00 if 1551 at #9, + sta USE4DY; $30 if 1551 at #8, and leave it at $01 otherwise + .endmacro + + .macro ENABLE_KERNAL_SERIAL_ROUTINES + sta TED_ROM_ENABLE + .endmacro + + .macro ENABLE_KERNAL_SERIAL_ROUTINES_Y + sta TED_ROM_ENABLE + .endmacro + + .macro ENABLE_ALL_RAM + sta TED_RAM_ENABLE + .endmacro + + .macro ENABLE_ALL_RAM_Y + sty TED_RAM_ENABLE + .endmacro + + .macro GET_MEMCONFIG + lda #ROM_IS_ENABLED + and TED_CHARGEN_ADDR + .endmacro + + .macro SET_MEMCONFIG + .local use_ram + + sta TED_RAM_ENABLE + beq use_ram + sta TED_ROM_ENABLE +use_ram: + .endmacro + + .macro SET_MEMCONFIG_Y + .local use_ram + + sty TED_RAM_ENABLE + beq use_ram + sty TED_ROM_ENABLE +use_ram: + .endmacro +.endif; !LOAD_VIA_KERNAL_FALLBACK + + +IO_PORT_DIR_COMMON = IO_PORT_SERIAL_DATA_IN_INPUT | IO_PORT_SERIAL_CLK_IN_INPUT | IO_PORT_CST_MTR_OUTPUT | IO_PORT_SERIAL_ATN_OUT_OUTPUT | IO_PORT_SERIAL_CLK_OUT_OUTPUT | IO_PORT_SERIAL_DATA_OUT_OUTPUT; $0f + + ; effectively, this is the KERNAL flag: + ; 0 = input = KERNAL, + ; 1 = output = loader +IO_PORT_DIR_KERNAL = IO_PORT_DIR_COMMON | IO_PORT_CST_RD_INPUT ; $0f + +IO_PORT_DIR_OPERATE = IO_PORT_DIR_COMMON | IO_PORT_CST_RD_OUTPUT; $1f + +.macro INSTALL_IDLE + lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f + sta IO_PORT + lda #IO_PORT_DIR_OPERATE + sta IO_PORT_DIRECTION +.endmacro + +.macro CLEAR store; store is ignored + lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT); $0c + sta IO_PORT +.endmacro + +.macro CLOSE_FILE + SYNC + ldx #IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_ATN_OUT) | IO_PORT_SERIAL_CLK_OUT | (0 & IO_PORT_SERIAL_DATA_OUT); $0a + stx IO_PORT +: dex + bne :- + lda IO_PORT + asl + IDLE +.endmacro + +.macro SYNC + lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | (0 & IO_PORT_SERIAL_DATA_OUT); $0e + sta IO_PORT +.endmacro + +.macro PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + .local singleclk + + lda TED_CHARGEN_ADDR + and #FORCE_SINGLE_CLOCK + pha + bne singleclk + lda #FORCE_SINGLE_CLOCK + php + sei ; 2 + ora TED_CHARGEN_ADDR ; 4 + sta TED_CHARGEN_ADDR ; 4 + plp ; 4 +singleclk: ; = 14 +.endmacro + +.macro POP_CLOCKCONFIG + .local singleclk + + pla + bne singleclk + lda #255 - FORCE_SINGLE_CLOCK + php + sei ; 2 + and TED_CHARGEN_ADDR ; 4 + sta TED_CHARGEN_ADDR ; 4 + plp ; 4 +singleclk: ; = 14 +.endmacro + +.macro SENDBYTE sendstore + ; does not clobber y + + .local sendbyte + .local bitset + + tax + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + txa + ldx #$07 +sendbyte: lsr + pha + lda IO_PORT + ora #IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT + bcs bitset + and #255 - IO_PORT_SERIAL_CLK_OUT +bitset: eor #IO_PORT_SERIAL_DATA_OUT + sta IO_PORT + pla + dex + bpl sendbyte + POP_CLOCKCONFIG +.endmacro + +.macro RECEIVEBYTE + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + lda #$01 +: pha + pla + pha + lda #IO_PORT_SERIAL_CLK_OUT + eor IO_PORT + ldx #IO_PORT_SERIAL_DATA_IN + cpx IO_PORT + sta IO_PORT + pla + rol + bcc :- + tax + POP_CLOCKCONFIG + txa +.endmacro + +.macro SET_FLAGS_N_DATA_V_CLK + bit IO_PORT +.endmacro + +CLOCK = IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT) +CLOCK_ATN_HI = CLOCK | IO_PORT_SERIAL_ATN_OUT ; 1st and 3rd bit pairs; $0c +CLOCK_ATN_LO = CLOCK | (0 & IO_PORT_SERIAL_ATN_OUT); 2nd and 4th bit pairs; $08 + +.macro ENABLE_WAITBUSY_KERNAL + lda #.lobyte(~IO_PORT_SERIAL_CLK_OUT) + and IO_PORT + sta IO_PORT +.endmacro + +.macro INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + lda #IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_ATN_OUT) | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT); $08 + sta IO_PORT +.endmacro + +.macro IDLE + ldx #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f + stx IO_PORT +.endmacro + +.if LOAD_VIA_KERNAL_FALLBACK + KERNALFILENO = 2 + + .macro CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN + lda IO_PORT + and #.lobyte(~(IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT)) + ora #IO_PORT_SERIAL_ATN_OUT + sta IO_PORT + .endmacro +.endif + +.macro BRANCH_IF_INSTALLED to + lda #IO_PORT_DIR_KERNAL + cmp IO_PORT_DIRECTION + bne to +.endmacro + +.macro BRANCH_IF_NOT_INSTALLED to + lda #IO_PORT_DIR_KERNAL + cmp IO_PORT_DIRECTION + beq to +.endmacro + +.macro CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to + jsr CHECKPARALLEL + bcc to +.endmacro + +.macro BRANCH_IF_DRIVE_PARALLEL to + lda USE4DY + lsr + bcc to +.endmacro + +.macro POLL_BLOCK idle_eof, block_not_yet_ready, device_not_present + lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f + cmp IO_PORT + beq idle_eof; branches with carry set on idle/eof + + clc + lda #diskio::status::DEVICE_NOT_PRESENT; $fe + SET_FLAGS_N_DATA_V_CLK + bvc block_not_yet_ready + sec + bmi device_not_present +.endmacro + +.macro SEND_BLOCK_SIGNAL + ldy #CLOCK_ATN_LO; use y to ensure that y < $fe when + sty IO_PORT ; calling getbyte for the 2 control bytes + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + pha; delay + pla +.endmacro + +.macro RECEIVE_SETUP + ; nothing to do +.endmacro + +.macro RECEIVE store, out + .local loop + + ; 16 cycles per bitpair ~ 18 cycles at 1 MHz = 18 µs + ; PAL: 16 / 886723 Hz = 18.04 µs + ; NTSC: 16 / 894886 Hz = 17.88 µs + +loop: ldx #CLOCK_ATN_HI ; 2 + lda IO_PORT ; 3 + stx IO_PORT; ATN high ; 3 + ; = 16 + + iny ; 2 + beq out ; 2 + lsr ; 2 + lsr ; 2 + ldx #CLOCK_ATN_LO ; 2 + eor IO_PORT ; 3 + stx IO_PORT; ATN low ; 3 + ; = 16 + + lsr ; 2 + lsr ; 2 + nop ; 2 - delay + nop ; 2 - delay + ldx #CLOCK_ATN_HI ; 2 + eor IO_PORT ; 3 + stx IO_PORT; ATN high ; 3 + ; = 16 + + lsr ; 2 + lsr ; 2 + nop ; 2 - delay + eor #CLOCK_ATN_HI | (CLOCK_ATN_LO >> 2) ; 2 + ldx #CLOCK_ATN_LO ; 2 + eor IO_PORT ; 3 + stx IO_PORT; ATN low ; 3 + ; = 16 + + store ; 5 - sta mem16,y + jmp loop ; 3 +out: +.endmacro + +.macro STOREBYTE_ALLRAM +storebytio: sta $0000,y +.endmacro + +.macro ENDGETBLOCK + POP_CLOCKCONFIG +.endmacro + +.macro SET_IO_KERNAL + INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + lda #IO_PORT_DIR_KERNAL + sta IO_PORT_DIRECTION +.endmacro + +.endif; !_HAL_C16_INC_ diff --git a/loader/src/hal/hal-c64-c128.inc b/loader/src/hal/hal-c64-c128.inc new file mode 100644 index 0000000..fd2e13c --- /dev/null +++ b/loader/src/hal/hal-c64-c128.inc @@ -0,0 +1,607 @@ + +.ifndef _HAL_C64_C128_INC_ +_HAL_C64_C128_INC_ = 1 + +.include "cia.inc" +.include "vic.inc" + +.macro CHECK_INSTALL_END_ADDRESS + .assert * <= $d000, error, "Install code exceeds $d000, please make sure the DISKIO_INSTALL segment ends below $d000" +.endmacro + +.if LOAD_VIA_KERNAL_FALLBACK + .macro CHECK_RESIDENT_START_ADDRESS + RESIDENT_START_ADDRESS = * + .endmacro + + .macro CHECK_RESIDENT_END_ADDRESS + .assert (RESIDENT_START_ADDRESS > ENABL) || (* < ENABL), error, "Resident code crosses KERNAL RS232 state variable ENABL" + + .assert * <= $d000, error, "Resident code exceeds $d000, please make sure the DISKIO segment ends below $d000" + .endmacro +.else + .macro CHECK_RESIDENT_START_ADDRESS + .assert (* <= $d000) || (* >= $e000), error, "Resident code resides at $d000..$dfff, please make sure the DISKIO segment does not overlap with that memory range" + .endmacro + + .macro CHECK_RESIDENT_END_ADDRESS + .assert (* <= $d000) || (* >= $e000), error, "Resident code resides at $d000..$dfff, please make sure the DISKIO segment does not overlap with that memory range" + .endmacro +.endif + + +.macro OK_CLC + ; use illegals to save size + alr #diskio::status::OK; $00, clc = all ok +.endmacro + +.macro PREPARE_DRIVE_DISTURBANCE_VALIDATION + ; nothing to do +.endmacro + +.macro BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS to + ; nothing to do +.endmacro + + +.if LOAD_VIA_KERNAL_FALLBACK + .macro PREPARE_PARALLEL_CHECK + ; nothing to do + .endmacro +.endif + +.if PLATFORM <> diskio::platform::COMMODORE_128 + + .if LOAD_VIA_KERNAL_FALLBACK + .macro ENABLE_KERNAL_SERIAL_ROUTINES + lda #MEMCONFIG_IO_KERNAL_BASIC + sta IO_PORT + .endmacro + + .macro ENABLE_KERNAL_SERIAL_ROUTINES_Y + ldy #MEMCONFIG_IO_KERNAL_BASIC + sty IO_PORT + .endmacro + .endif; LOAD_VIA_KERNAL_FALLBACK + + .if LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK + + .macro BUFFER_MEMCONFIG + lda IO_PORT + pha + .endmacro + + .macro RESTORE_MEMCONFIG_Y + tay + pla + sta IO_PORT + tya + .endmacro + + .macro ENABLE_IO_SPACE + lda #MEMCONFIG_IO + sta IO_PORT + .endmacro + + .macro ENABLE_IO_SPACE_X + ldx #MEMCONFIG_IO + stx IO_PORT + .endmacro + + .macro ENABLE_IO_SPACE_Y + ldy #MEMCONFIG_IO + sty IO_PORT + .endmacro + + .macro ENABLE_ALL_RAM + lda #MEMCONFIG_ALL_RAM + sta IO_PORT + .endmacro + + .macro ENABLE_ALL_RAM_X + ldx #MEMCONFIG_ALL_RAM + stx IO_PORT + .endmacro + + .macro ENABLE_ALL_RAM_Y + ldy #MEMCONFIG_ALL_RAM + sty IO_PORT + .endmacro + + .macro GET_MEMCONFIG + lda IO_PORT + .endmacro + + .macro SET_MEMCONFIG + sta IO_PORT + .endmacro + + .macro SET_MEMCONFIG_X + stx IO_PORT + .endmacro + + .macro SET_MEMCONFIG_Y + sty IO_PORT + .endmacro + .endif; !(LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK) + +.else; PLATFORM = diskio::platform::COMMODORE_128 + + .include "mmu.inc" + + .macro DISABLE_BURST_MODE + lda #IOMODE_OUTPUT + ora CIA1_CRA + sta CIA1_CRA + .endmacro + + .if LOAD_VIA_KERNAL_FALLBACK + .macro ENABLE_KERNAL_SERIAL_ROUTINES + lda #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE + sta MMU_CR + .endmacro + + .macro ENABLE_KERNAL_SERIAL_ROUTINES_Y + ldy #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE + sty MMU_CR + .endmacro + + .macro START_BURST_LOAD + bit CIA1_ICR + lda #SERIAL_CLK_OUT + eor CIA2_PRA + sta CIA2_PRA + .endmacro + + .macro GET_BURST_BYTE + .local waitburst + + lda #SERIAL_IRQ +waitburst: bit CIA1_ICR + beq waitburst + ldy CIA1_SDR + lda #SERIAL_CLK_OUT + php + sei + eor CIA2_PRA + sta CIA2_PRA + plp + tya + .endmacro + .endif; LOAD_VIA_KERNAL_FALLBACK + + .if LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK + + .macro BUFFER_MEMCONFIG + lda MMU_CR + pha + .endmacro + + .macro RESTORE_MEMCONFIG_Y + tay + pla + sta MMU_CR + tya + .endmacro + + .macro ENABLE_IO_SPACE + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE + sta MMU_CR + .endmacro + + .macro ENABLE_IO_SPACE_X + ldx #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE + stx MMU_CR + .endmacro + + .macro ENABLE_IO_SPACE_Y + ldy #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE + sty MMU_CR + .endmacro + + .macro ENABLE_ALL_RAM + lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM + sta MMU_CR + .endmacro + + .macro ENABLE_ALL_RAM_X + ldx #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM + stx MMU_CR + .endmacro + + .macro ENABLE_ALL_RAM_Y + ldy #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM + sty MMU_CR + .endmacro + + .macro GET_MEMCONFIG + lda MMU_CR + .endmacro + + .macro SET_MEMCONFIG + sta MMU_CR + .endmacro + + .macro SET_MEMCONFIG_X + stx MMU_CR + .endmacro + + .macro SET_MEMCONFIG_Y + sty MMU_CR + .endmacro + .endif; !(LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK) + +.endif; PLATFORM = diskio::platform::COMMODORE_128 + +; CIA2 DDRA ($DD02) definitions +CIA2_DDRA_COMMON = CIA_SERIAL_DATA_IN_INPUT | CIA_SERIAL_CLK_IN_INPUT | CIA_VIC2_BANK_OUTPUT; $03 + ; effectively, this is the KERNAL flag: + ; 0 = input = loader, +; DATA OUT, CLK OUT, ATN OUT are clear, RS232_TXD is clear ; 1 = output = KERNAL +CIA2_DDRA_KERNAL = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $3f + +; DATA OUT, CLK OUT, ATN OUT are set, RS232_TXD input, all bits except VIC bank bits are inputs so that $DD00 writes do not change the bus state +CIA2_DDRA_IDLE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_INPUT | CIA_RS232_TXD_INPUT ; $03 + +; DATA OUT and CLK OUT are clear, ATN OUT is set, RS232_TXD is output +CIA2_DDRA_CLEAR = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $37 + +; DATA OUT, CLK OUT and ATN OUT are clear, RS232_TXD is input +CIA2_DDRA_RECEIVE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $3b + +; DATA OUT is clear, CLK OUT and ATN OUT are set, RS232_TXD is input +CIA2_DDRA_SYNC = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $23 + +; DATA OUT and ATN OUT are clear, CLK OUT is set, RS232_TXD is input +CIA2_DDRA_CLOSE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $2b + +.macro INSTALL_IDLE + lda #CIA2_DDRA_IDLE + sta CIA2_DDRA +.endmacro + +.macro CLEAR store + lda #CIA2_DDRA_CLEAR + .ifnblank store + store: + .endif + sta CIA2_DDRA +.endmacro + +.macro CLOSE_FILE + lda #CIA2_DDRA_SYNC + sta CIA2_DDRA + ldx #CIA2_DDRA_CLOSE + stx CIA2_DDRA +: dex + bne :- + lda CIA2_PRA + asl + IDLE +.endmacro + +.macro SYNC + lda #CIA2_DDRA_SYNC + sta CIA2_DDRA +.endmacro + +.macro SET_FLAGS_N_DATA_V_CLK + bit CIA2_PRA +.endmacro + +.macro PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + lda VIC2_C128_CLOCK + pha + lda #0 + sta VIC2_C128_CLOCK +.endmacro + +.macro POP_CLOCKCONFIG + pla + sta VIC2_C128_CLOCK +.endmacro + +.macro SENDBYTE sendstore + ; does not clobber y + + .local sendbyte + .local bitset + + .if USE_2_MHZ + tax + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + txa + .endif + ldx #$07 +sendbyte: lsr + pha + .if NTSC_COMPATIBILITY + nop + .endif + .ifblank sendstore + pha; delay + lda CIA2_DDRA + and #255 - (SERIAL_ATN_OUT + SERIAL_CLK_OUT) + bcs bitset + ora #SERIAL_CLK_OUT +bitset: eor #SERIAL_DATA_OUT + sta CIA2_DDRA + pla; delay + .else + lda GETBYTE_CLOCK_ATN_HI + and #255 - (SERIAL_ATN_OUT + SERIAL_CLK_OUT) + bcs bitset + ora #SERIAL_CLK_OUT +bitset: eor #SERIAL_DATA_OUT + jsr sendstore + .endif + pla + dex + bpl sendbyte + .if USE_2_MHZ + POP_CLOCKCONFIG + .endif +.endmacro + +.macro RECEIVEBYTE + .if USE_2_MHZ + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + .endif + lda #$01 +: pha + pla + pha + lda #SERIAL_CLK_OUT + eor CIA2_DDRA + ldx #SERIAL_DATA_IN + cpx CIA2_PRA + sta CIA2_DDRA + pla + rol + bcc :- + .if USE_2_MHZ + tax + POP_CLOCKCONFIG + txa + .endif +.endmacro + +; RS232 TXD set to output so this bit won't interfere with byte fetch from serial bus +CLOCK = CIA2_DDRA_COMMON | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $37 +CLOCK_ATN_HI = CLOCK | CIA_SERIAL_ATN_OUT_INPUT ; 1st and 3rd bit pairs, $37 +CLOCK_ATN_LO = CLOCK | CIA_SERIAL_ATN_OUT_OUTPUT; 2nd and 4th bit pairs, $3f + +.macro ENABLE_WAITBUSY_KERNAL + lda #.lobyte(~(SERIAL_DATA_OUT | SERIAL_CLK_OUT)) + and CIA2_PRA + sta CIA2_PRA +.endmacro + +.if LOAD_VIA_KERNAL_FALLBACK + KERNALFILENO = 2 + + .macro CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN + lda CIA2_PRA + and #.lobyte(~(SERIAL_DATA_OUT | SERIAL_CLK_OUT)) + ora #SERIAL_ATN_OUT + sta CIA2_PRA + .endmacro +.endif + +.macro BRANCH_IF_INSTALLED to + lda #CIA2_DDRA_KERNAL + cmp CIA2_DDRA + bne to +.endmacro + +.macro BRANCH_IF_NOT_INSTALLED to + lda #CIA2_DDRA_KERNAL + eor CIA2_DDRA + beq to +.endmacro + +.macro CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to + lda IOPEN + 1 + cmp #.hibyte($df00); serial: AR fastload vectors point to $DFxx, parallel: IDE64 vectors point to $DExx, IEEE-488 interfaces to $Cxxx + bcc to +.endmacro + +.macro BRANCH_IF_DRIVE_PARALLEL to + CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to +.endmacro + +.macro IDLE + ldx #CIA2_DDRA_IDLE + stx CIA2_DDRA +.endmacro + +.macro POLL_BLOCK idle_eof, block_not_yet_ready, device_not_present + lda #CIA2_DDRA_IDLE + cmp CIA2_DDRA + beq idle_eof; branches with carry set on idle/eof + + ;clc + lda #diskio::status::DEVICE_NOT_PRESENT; $fe + SET_FLAGS_N_DATA_V_CLK + bvc block_not_yet_ready + sec + bmi device_not_present +.endmacro + +.macro SEND_BLOCK_SIGNAL + ldy #CIA2_DDRA_RECEIVE; use y to ensure that y < $fe when + sty CIA2_DDRA ; calling getbyte for the 2 control bytes + + .if USE_2_MHZ + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + .else + pha; delay + pla + .endif + .if (PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE + lda #OPC_BVS + sta burstwait + .if LOAD_UNDER_D000_DFFF + sta burstwaitio + .endif + .endif; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE) +.endmacro + +.macro RECEIVE_SETUP + ;lda #CLOCK_ATN_HI; = CIA2_DDRA_IDLE + sta GETBYTE_CLOCK_ATN_HI +.endmacro + +.macro RECEIVE store, out + .local getloop + .local merge + .local entry + + sec; add at label entry below must yield bit 8 set + + .if PLATFORM = diskio::platform::COMMODORE_128 + .local burstloop + .local storeburst + .local waitburst + .local noburst + + .if .not .xmatch (store, STOREBYTE_ALLRAM) + .define burstswit burstwait + .else + .define burstswit burstwaitio + .endif + + bit CIA1_CRA + bvs noburst; branch if IO mode is set to output + + .if .not .xmatch (store, STOREBYTE_ALLRAM) + lda storebyte + 0 + sta storeburst + 0 + lda storebyte + 1 + sta storeburst + 1 + lda storebyte + 2 + sta storeburst + 2 + .else + lda storebytio + 1 + sta storbytiob + 1 + lda storebytio + 2 + sta storbytiob + 2 + .endif + iny + +burstloop: lda CIA2_DDRA ; use the ATN line for the data taken signal in + eor #SERIAL_ATN_OUT ; order to keep passive drives from leaving the + tax ; silencing loop after too long a period of ATN clear + + .if USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda #OPC_BVC ^ OPC_BVS; use the CLK line for the incoming data sent signal rather + eor burstswit ; than the SERIAL_IRQ flag in CIA1_ICR ($dc0d) in order to + sta burstswit ; avoid interference by CIA1 interrupt handlers +waitburst: bit CIA2_PRA +burstswit: bvs waitburst ; wait for incoming data sent signal + + .else; !USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda #SERIAL_IRQ +waitburst: bit CIA1_ICR + beq waitburst + + .endif; !USE_ASYNCHRONOUS_BURST_HANDSHAKE + + lda CIA1_SDR + stx CIA2_DDRA ; signal data taken + + .if .not .xmatch (store, STOREBYTE_ALLRAM) +storeburst: sta $0000,y + .else + ENABLE_ALL_RAM_X +storbytiob: sta $0000,y + ENABLE_IO_SPACE_X + .endif + iny + bne burstloop + + lda #CLOCK_ATN_HI + sta CIA2_DDRA + bne out; jmp + +noburst = entry + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + ;sec; add below must yield bit 3 set + bcs entry; jmp + + ; note: between each store to CIA2_DDRA to signal ready for the next bit pair, + ; 10 cycles must pass before read access of CIA2_PRA: + ; the drive needs max. 14 cycles to respond with a loop in the form of 'bit VIA1_PRB : bpl/bmi * - 3 : sta VIA1_PRB' - + ; this means that 18 cycles per bit pair are the minimum + +getloop: lsr ; 2 - -3210HHH + lsr ; 2 - --3210HH:H + .if NTSC_COMPATIBILITY + ldx GETBYTE_CLOCK_ATN_HI ; $37 ; 3 + .else + ldx #CLOCK_ATN_HI ; $37 ; 2 + .endif + ora CIA2_PRA - CLOCK_ATN_HI,x ; 5 - 543210HH - delay + stx CIA2_DDRA; sync 3: set ATN high ; 4 + ; = 18 + + ror ; 2 - H543210H:H + ror ; 2 - HH543210:H + ldx #CLOCK_ATN_LO ; $3f ; 2 + sax merge + $01 ; 4 - --543210 + .if NTSC_COMPATIBILITY + and CIA2_PRA - CLOCK_ATN_LO,x ; 4 - 7654.2.. + .else + and CIA2_PRA ; 4 - 7654.2.. + .endif + stx CIA2_DDRA; sync 4: set ATN low ; 4 + ; = 18 + +merge: ora #$00 ; 2 - 76543210 + store ; 5 - sta mem16,y +entry: lax GETBYTE_CLOCK_ATN_HI ; $37 ; 3 + .if NTSC_COMPATIBILITY + adc CIA2_PRA - CLOCK_ATN_HI,x ; 5 - 10HHH... - carry must be set + .else + adc CIA2_PRA ; 4 - 10HHH... - carry must be set + .endif + stx CIA2_DDRA; sync 1: set ATN high ; 4 + ; = 18 + + iny ; 2 + beq out ; 2 - clc + lsr ; 2 - -10HHH.. + lsr ; 2 - --10HHH. + ldx #CLOCK_ATN_LO ; $3f ; 2 + .if NTSC_COMPATIBILITY + ora CIA2_PRA - CLOCK_ATN_LO,x ; 5 - 3210HHH. + .else + ora CIA2_PRA ; 4 - 3210HHH. + .endif + stx CIA2_DDRA; sync 2: set ATN low ; 4 + ; = 18 + + jmp getloop ; 3 +.endmacro + +.macro STOREBYTE_ALLRAM + ENABLE_ALL_RAM_X +storebytio: sta $0000,y + ENABLE_IO_SPACE_X +.endmacro + +.macro ENDGETBLOCK + .if USE_2_MHZ + POP_CLOCKCONFIG + .endif +.endmacro + +.macro SET_IO_KERNAL + lda #CIA2_DDRA_KERNAL + sta CIA2_DDRA +.endmacro + +.endif; !_HAL_C64_C128_INC_ diff --git a/loader/src/hal/hal.inc b/loader/src/hal/hal.inc new file mode 100755 index 0000000..fa20ad2 --- /dev/null +++ b/loader/src/hal/hal.inc @@ -0,0 +1,33 @@ + +.ifndef _HAL_INC_ +_HAL_INC_ = 1 + +.if PLATFORM = diskio::platform::COMMODORE_16 + .include "hal/hal-c16.inc" +.else; PLATFORM <> diskio::platform::COMMODORE_16 + .include "hal/hal-c64-c128.inc" +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + + +.macro DO_UNINSTALL + .local waituninst + + SET_IO_KERNAL +waituninst: SET_FLAGS_N_DATA_V_CLK + bpl waituninst + bvc waituninst +.endmacro + +; block ready queries are the same for the loader's and KERNAL's protocols + +.macro BRANCH_IF_BLOCK_READY to + SET_FLAGS_N_DATA_V_CLK + bvs to +.endmacro + +.macro BRANCH_IF_BLOCK_NOT_READY to + SET_FLAGS_N_DATA_V_CLK + bvc to +.endmacro + +.endif; !_HAL_INC_ diff --git a/loader/src/install.s b/loader/src/install.s new file mode 100755 index 0000000..2141bfe --- /dev/null +++ b/loader/src/install.s @@ -0,0 +1,1442 @@ + +.fileopt comment, "Loader install code portion" +.fileopt compiler, "CA65" +.fileopt author, "Gunnar Ruthenberg" + +__NO_LOADER_SYMBOLS_IMPORT = 1 +.include "loader.inc" +.include "../version.inc" + +.include "cpu.inc" +.include "cia.inc" +.include "vdc.inc" + +.include "basic.inc"; for PETSCII_RETURN +.include "kernal.inc" + +CBM1581_8 = $a6e9; the '8' in the 1581's ID string +FD_F_HD_H = $fea4; the 'f' in the CMD FD 2000/4000's ID string or the 'h' in the CMD HD's ID string + + +.include "hal/hal.inc" + +.include "drives/drivecode-common.inc" + +.importzp loadaddrlo + + +.if ONLY_1541_AND_COMPATIBLE = 0 +.import cmdfdfix0 +.import cmdfdfix1 +.import cmdfdfix2 + .if !DISABLE_WATCHDOG +.import cmdfdfix3 +.import cmdfdfix4 + .if ::PLATFORM = diskio::platform::COMMODORE_128 +.import cmdfdfix5 +.import cmdfdfix6 + .endif ; ::PLATFORM = diskio::platform::COMMODORE_128 + .endif; !DISABLE_WATCHDOG +.endif; ONLY_1541_AND_COMPATIBLE = 0 + + +USE_GENERIC_DRIVE = 0 + + +.macro itoa4 value + .if (value & $0f > 9) + .byte (value & $0f) + 'a' - 10 + .else + .byte (value & $0f) + '0' + .endif +.endmacro + +.macro itoa1 value + itoa4 value <> 0 +.endmacro + +.macro itoa8 value + itoa4 value >> 4 + itoa4 value & $0f +.endmacro + +.macro itoa16 value + itoa8 value >> 8 + itoa8 value & $ff +.endmacro + + +.segment "DISKIO_INSTALL" + +.ifdef INSTADDR + .org INSTADDR - 2 + .word * + 2; load address +.endif + +.export install + + ; Install the loader + + ; in: nothing + ; out: c - set on error + ; a - status + ; x - drive type (one of diskio::drivetype) + ; y - if status is diskio::status::OK, zp address of version string address +install: jmp doinstall + + ; unfortunately, scopes must be defined before using them, + ; this is why the actual install code is moved to after the drive code +.scope cbm1541 +drivecode41: + .include "drives/drivecode1541.s" + + .exportzp ID041 + .exportzp CURRTRACK41 + .exportzp NUMFILES41 + .exportzp SECTORLINKTABLE41 + + .export topofstack41 + .export idleloop41 + .export getbytecmp41 + .export BLOCKBUFFER41 + + ; symbols used by the saver + .exportzp V1B41 + .exportzp V2B41 + .exportzp LEDSTATE41 + .exportzp FILESECTOR41 + .exportzp FILENAME41 + .exportzp LINKTRACK41 + .exportzp LINKSECTOR41 + .exportzp REQUESTEDSECTOR41 + + .export trkseek41 + .export initlink41 + .export sertoraw41 + .export getbytewdog41 + .export getbyte41 + .export getbyterts41 + .export setbv2b41 + .export getblock41 + .export idxloop41 + .export wdogentr41 + .export findfile41 + .export loadfile41 +.endscope + +.if ONLY_1541_AND_COMPATIBLE = 0 + +.scope cbm1571 +drivecode71: + .include "drives/drivecode1571.s" + + .exportzp SECTORLINKTABLE71 + .exportzp CUSTOMZPBUFFER71 + .exportzp CUSTOMUPLOADSIZE71 + + .export topofstack71 + .export idleloop71 + .export BLOCKBUFFER71 + + ; symbols used by the saver + .exportzp CURRTRACK71 + .exportzp LEDSTATE71 + .exportzp CLEARSECTORLINKTABLE71 + .exportzp FILENAME71 + .exportzp LINKTRACK71 + .exportzp LINKSECTOR71 + .exportzp DIRSECTORS71 + + .export bsetv2b71 + .export trkseek71 + .export initlink71 + .export onemhz71 + .export getblock71 + .export findfile71 + .export loadfile71 + .export idxloop71 +.endscope + +.scope cbm1581 +drivecode81: + .include "drives/drivecode1581.s" + + .exportzp CUSTOMPARAM81 + + .export dcodinit81 + .export BLOCKBUFFER81 + .export CUSTOMRECEIVE81 + .export loadfile81 + + ; symbols used by the saver + .export filename81 + .export swapzp81 + .export getblock81 + .export initwdog81 + .export enablwdg81 + .export initcntr81 + .export bsyledon81 + .export findfile81 + .export DIRTRACKS81 +.endscope + +.endif; ONLY_1541_AND_COMPATIBLE = 0 + +doinstall: lda #.lobyte(version) + sta loadaddrlo + lda #.hibyte(version) + sta loadaddrlo + 1 + + BRANCH_IF_NOT_INSTALLED :+ + jmp isinstalld + +: php; I flag buffer + + jsr CLALL + +.if PLATFORM = diskio::platform::COMMODORE_128 + + ; set data and filename banks to current program bank + lda MMU_CR + asl + rol + rol + and #$03; BA + tax ; FNBANK + jsr SETBANK + +.endif; PLATFORM = diskio::platform::COMMODORE_128 + + ; try the drive as denoted by FA (current drive) first + lda FA + cmp #MIN_DEVICE_NO + bcc :+ + cmp #MAX_DEVICE_NO + 1 + bcc :++ +: lda #MIN_DEVICE_NO; FA does not contain a drive address (MIN_DEVICE_NO..MAX_DEVICE_NO), try MIN_DEVICE_NO first +: + ; find first available drive, + ; this is done via the high-level open/read/close routines, + ; so non-serial bus devices will also respond + sta FA + +find1stdrv: pha; buffer active drive device number + + lda #0 + tax + tay + jsr SETNAM + lda #COMMAND_ERROR_CHANNEL + ldx FA + tay + jsr SETLFS + jsr OPEN + bcc openokay + + cmp #OPEN_DEVICENOTPRESENT + beq trynextdev + tax + pla; buffered active drive device number + lda #diskio::status::GENERIC_KERNAL_ERROR + bne installerr; jmp + +trynextdev: ; device not present, try next address + lda #COMMAND_ERROR_CHANNEL + jsr CLOSE + jsr CLRCH + ldx FA + inx + cpx #MAX_DEVICE_NO + 1 + bne :+ + ldx #MIN_DEVICE_NO +: stx FA + pla; buffered active drive device number + cmp FA + bne find1stdrv + + lda #diskio::status::DEVICE_NOT_PRESENT + ldx #diskio::drivetype::DEVICE_NONE +installerr: ldy #loadaddrlo + plp; I flag restore + sec + rts + +openokay: ldx #COMMAND_ERROR_CHANNEL + jsr CKOUT + jsr READSS + bne trynextdev + jsr CLRCH + ; read error channel, this also stops potentially blinking error LED + ldx #COMMAND_ERROR_CHANNEL + jsr CHKIN +: jsr READSS + bne :+ + jsr BASIN + jmp :- +: lda #COMMAND_ERROR_CHANNEL + jsr CLOSE + jsr CLRCH + + pla; buffered active drive device number, leave FA at first detected present device + +.if USE_GENERIC_DRIVE + jmp usegeneric +.endif + +.if ONLY_1541_AND_COMPATIBLE = 0 + ; check if drive allows code upload and execution + jsr chkdrvcode + beq notgeneric + +usegeneric: ; no compatible drive found + lda #diskio::status::DEVICE_INCOMPATIBLE + jmp nodrvcode + +notgeneric: ; check which model the drive is and upload corresponding drive code + jsr getmodel + + sty drivetype + tya + bmi usegeneric + lsr + lsr + lsr + lsr + tax + tya + and #diskio::drivetype::DRIVES_MASK + cmp #diskio::drivetype::DRIVES_1581_CMD + bne not1581cmd + + cpy #diskio::drivetype::DRIVE_CMD_HD + beq usegeneric + cpy #diskio::drivetype::DRIVE_1581 + beq is1581 + + lda #OPC_BIT_ABS + sta cmdfdfix0 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + lda #.lobyte($54); DIRTRACKFD + sta cmdfdfix1 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + lda #.hibyte($54); DIRTRACKFD + sta cmdfdfix2 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + .if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!DISABLE_WATCHDOG) + lda #$ff + ldy #.lobyte($1c05) + .endif + jmp iscmdfd + +is1581: lda #OPC_JMP_ABS + sta cmdfdfix0 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + lda #.lobyte($022b); DIRTRACK81 + sta cmdfdfix1 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + lda #.hibyte($022b); DIRTRACK81 + sta cmdfdfix2 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + .if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!DISABLE_WATCHDOG) + lda #COUNT_TA_UNDF | FORCE_LOAD | ONE_SHOT | TIMER_START + ldy #.lobyte(CIA_CRB) +iscmdfd: sta cmdfdfix3 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + sty cmdfdfix4 - cbm1581::drvcodebeg81 + cbm1581::drivecode81 + .else +iscmdfd: + .endif +not1581cmd: + lda dcodeselt0,x + sta dcodesel0 + lda dcodeselt1,x + sta dcodesel1 + lda dcodeselt2,x + sta dcodesel2 + lda dcodeselt3,x + sta dcodesel3 + lda dcodeselt4,x + sta dcodesel4 + lda dcodeselt5,x + sta dcodesel5 + lda dcodeselt6,x + sta dcodesel6 + lda dcodeselt7,x + sta dcodesel7 + lda dcodeselt8,x + sta dcodesel8 + lda dcodeselt9,x + sta family + lda dcodeselta,x + sta dirtrack + +.else; ONLY_1541_AND_COMPATIBLE + + jsr chkdrvcode + beq :+ + ; no compatible drive found + lda #diskio::status::DEVICE_INCOMPATIBLE + jmp nodrvcode + + ; check if 1541U +: jsr drvlistn + ldx #0 +: lda drvch1541u,x + jsr CIOUT + inx + cpx #drvchkued - drvch1541u + bne :- + jsr UNLSN + lda #.lobyte($0300) + ldx #.hibyte($0300) + jsr memreadbyt + bmi :+; branch if 1541U + lda #diskio::drivetype::DRIVE_1541 + SKIPWORD +: lda #diskio::drivetype::DRIVE_1541U + sta drivetype + + lda #.lobyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41) + sta dcodesel0 + lda #.hibyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41) + sta dcodesel1 + lda #.lobyte(cbm1541::drvprgend41 - cbm1541::drvcodeend41 + cbm1541::TRAMPOLINEOFFSET) + sta dcodesel2 + lda #.lobyte(cbm1541::drivecode41) + sta dcodesel3 + lda #.hibyte(cbm1541::drivecode41) + sta dcodesel4 + lda #.hibyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET) + sta dcodesel5 + lda #.lobyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET) + sta dcodesel6 + lda #.hibyte(cbm1541::dinstall) + sta dcodesel7 + lda #.lobyte(cbm1541::dinstall) + sta dcodesel8 + +.endif; ONLY_1541_AND_COMPATIBLE + + ; check if there is more than 1 drive on the serial bus, + ; upload silencing routines to the passive drives in order + ; to make sure the 2bit+ATN protocol can work alright, + ; detection is done via the low-level serial bus routines, + ; so non-serial bus devices won't respond + ; (1551 on Plus/4 does respond, though, so a little extra + ; treatment is done through the drive disturbance HAL macros) + + lda FA; active drive + pha + ldx #MIN_DEVICE_NO +checkbus: stx FA + pla + pha + cmp FA + beq jmpnodrive + + lda #0 + sta STATUS + PREPARE_DRIVE_DISTURBANCE_VALIDATION + jsr drvlistn + BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS jmpnodrive + jsr READSS + bpl :+ +jmpnodrive: jmp nodrive + + ; more than 1 drive on the bus or generic serial devices present +: jsr UNLSN + + ; upload and execute silencing routine +.if ONLY_1541_AND_COMPATIBLE = 0 + jsr chkdrvcode + beq mute + +mutefail: lda FA + ldx #'0' - 1 + sec +: inx + tay + sbc #10 + bcs :- + stx mutedevice + tya + adc #'0' + sta mutedevice + 1 + ldx #errormute - messages + jsr message + pla + sta FA + lda #diskio::status::TOO_MANY_DEVICES + jmp nodrvcode + +mute: jsr getmodel + tya + bmi mutefail + + tya + and #diskio::drivetype::DRIVES_MASK + cmp #diskio::drivetype::DRIVES_1581_CMD + beq mute81cmd + + pha +.endif; ONLY_1541_AND_COMPATIBLE = 0 + jsr drvlistn + ldx #0 +: lda drvsilencc,x + jsr CIOUT + inx + cpx #atnfallbck - drvsilencc + bne :- + jsr drvrelistn + ldx #0 +: lda atnfallbck,x + jsr CIOUT + inx + cpx #atnlo - atnfallbck + bne :- + jsr drvrelistn + ldx #0 +: lda atnlo,x + jsr CIOUT + inx + cpx #atnhi - atnlo + bne :- + jsr drvrelistn + ldx #0 +: lda atnhi,x + jsr CIOUT + inx + cpx #atnhiend - atnhi + bne :- +.if ONLY_1541_AND_COMPATIBLE = 0 + jsr UNLSN + pla + cmp #diskio::drivetype::DRIVES_157X + bne :++ + jsr drvlistn + ldx #0 +: lda drvslnc71,x + jsr CIOUT + inx + cpx #drvslnc71e - drvslnc71 + bne :- + beq mutexecute; jmp +: jsr drvlistn +.else; ONLY_1541_AND_COMPATIBLE = 0 + jsr drvrelistn +.endif; ONLY_1541_AND_COMPATIBLE = 0 + ldx #0 +: lda drvsilence,x + jsr CIOUT + inx + cpx #drvsilnced - drvsilence + bne :- + +.if ONLY_1541_AND_COMPATIBLE = 0 + beq mutexecute; jmp + +mute81cmd: cpy #diskio::drivetype::DRIVE_1581 + bne mutecmd + + jsr drvlistn + ldx #0 +: lda drvslnc81,x + jsr CIOUT + inx + cpx #drvslnc81e - drvslnc81 + bne :- + beq mutexecute; jmp + +mutecmd: cpy #diskio::drivetype::DRIVE_CMD_HD + beq mutecmdhd + + jsr drvlistn + ldx #0 +: lda drvslncfd,x + jsr CIOUT + inx + cpx #drvslncfde - drvslncfd + bne :- + beq mutexecute; jmp + +mutecmdhd: jsr drvlistn + ldx #0 +: lda drvslnchd,x + jsr CIOUT + inx + cpx #drvslnchde - drvslnchd + bne :- +.endif; ONLY_1541_AND_COMPATIBLE = 0 + +mutexecute: jsr drvrelistn + ldx #0 +: lda drvmuteme,x + jsr CIOUT + inx + cpx #drvmutemed - drvmuteme + bne :- + beq nodrive; jmp + +nodrvcode: pha; error code + +.if LOAD_VIA_KERNAL_FALLBACK + ; quicker head stepping + jsr drvlistn + ldx #6 +: lda drvfaststp,x + jsr CIOUT + dex + bpl :- + jsr UNLSN + + lda #.lobyte($e5c6) + ldx #.hibyte($e5c6) + jsr memreadbyt + cpx #'1' | $80; 71 + bne :++ + jsr drvlistn + ldx #4 +: lda twosided,x + jsr CIOUT + dex + bpl :- + jsr drvrelistn; make sure the drive is done + jsr UNLSN ; when leaving the init routine +: +.endif; LOAD_VIA_KERNAL_FALLBACK + + CHECK_AND_BRANCH_IF_DRIVE_PARALLEL drivepar + +.if PLATFORM = diskio::platform::COMMODORE_128 + lda burstflag + beq :+ + ldx #diskio::drivetype::DRIVE_GENERIC_BURST + SKIPWORD +: +.endif + ldx #diskio::drivetype::DRIVE_GENERIC_SERIAL + SKIPWORD +drivepar: ldx #diskio::drivetype::DRIVE_GENERIC_PARALLEL + pla; error code + plp; I flag restore + ldy #loadaddrlo +.if LOAD_VIA_KERNAL_FALLBACK + clc; this is not to be regarded as an error +.else + sec +.endif + rts + +nodrive: jsr UNLSN + ldx FA + inx + cpx #MAX_DEVICE_NO + 1 + beq :+ + jmp checkbus +: pla + sta FA; active drive + + ; install drive-side loader code +.if PLATFORM = diskio::platform::COMMODORE_128 + lda #0 + sta SERIAL +.endif + jsr drvlistn + ldx #0 +upload: sec + stx :+ + 1 +dcodesel2 = * + 1 + lda #0 +: sbc #0 + cmp #35 + bcc :+ + lda #35 +: sta drvrutmw + + ldy #6 +: lda drvrutmw - 1,y + jsr CIOUT + dey + bne :- + +dcodesel0 = * + 1 +dcodesel1 = * + 2 +: lda $c001,x + jsr CIOUT + inx + cpx dcodesel2 + beq :+ + iny + cpy #35 + bne :- + jsr drvrelistn + clc + lda #35 + adc drvrutmw + 2 + sta drvrutmw + 2 + bcc upload + inc drvrutmw + 1 + bne upload + +: jsr drvrelistn + ldx #0 +: lda droutrun,x + jsr CIOUT + inx + cpx #droutruned - droutrun + bne :- + jsr UNLSN + + INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT + +: SET_FLAGS_N_DATA_V_CLK + bvs :- + + CLEAR + +: SET_FLAGS_N_DATA_V_CLK + bvc :- + +dcodesel3 = * + 1 + ldy #0 +dcodesel4 = * + 2 +fastinst: lda $00,y + SENDBYTE + iny + bne :+ + inc fastinst + 2 +: cpy dcodesel0 + bne fastinst + lda fastinst + 2 + cmp dcodesel1 + bne fastinst + + INSTALL_IDLE + +.if PLATFORM = diskio::platform::COMMODORE_128 +burstflag = * + 1 + lda #0 + .if ONLY_1541_AND_COMPATIBLE = 0 + bne burst + .endif + DISABLE_BURST_MODE +burst: +.endif; PLATFORM = diskio::platform::COMMODORE_128 + + ; if 1541U is detected, it runs on buggy firmware + lda drivetype + cmp #diskio::drivetype::DRIVE_1541U + bne :+ + ldx #warnemubug - messages + jsr message + +: plp; I flag restore + +isinstalld: lda #diskio::status::OK +drivetype = * + 1 + ldx #0 + ldy #loadaddrlo + clc + rts + +message: lda BORDERCOLOUR + pha + lda BGCOLOUR + pha + lda #COLOUR_RED + sta BORDERCOLOUR + sta BGCOLOUR +.if PLATFORM = diskio::platform::COMMODORE_16 + lda TED_CTRL1 + pha + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $1b + sta TED_CTRL1 + lda TED_CTRL2 + pha + and #.lobyte(~MULTICOLOUR_MODE) + ora #COLUMNS_40 + sta TED_CTRL2 + lda TED_BITMAP_ADDR + pha + ora #CHARSET_BITMAP_IN_ROM + sta TED_BITMAP_ADDR + lda TED_CHARGEN_ADDR + pha + and #.lobyte(~CHARGEN_ADDR_MASK) + ora #MAKE_CHARGEN_ADDR(CHARSET_ADDR_UPPERLOWER) + sta TED_CHARGEN_ADDR + lda TED_SCREEN_ADDR + pha + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR($0c00) + sta TED_SCREEN_ADDR + lda PALETTE + pha + lda #PALETTE_DEFAULT + sta PALETTE +.else; PLATFORM <> diskio::platform::COMMODORE_16 + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #FG_BG + sta VDC_CR +: bit VDC_SR; wait until update ready + bpl :- + lda VDC_DR + pha + lda #RED + sta VDC_DR + .endif ; PLATFORM = diskio::platform::COMMODORE_128 + lda VIC2_CTRL1 + and #.lobyte(~RASTERLINE_BIT8) + pha + lda VIC2_CTRL2 + pha + lda VIC2_ADDR + pha + lda CIA2_PRA + and #VIC2_BANK + pha + lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $1b + sta VIC2_CTRL1 + lda #SINGLECOLOUR_MODE | COLUMNS_40 | SCROLLX_0; $08 + sta VIC2_CTRL2 + lda #VIC2_MAKE_ADDR($0400, CHARSET_ADDR_UPPERLOWER) + sta VIC2_ADDR + lda #(VIC2_MAKE_BANK $0400) & VIC2_BANK + sta CIA2_PRA +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + lda DFLTO + pha + lda COLOR + pha + lda #DEVICE_SCREEN + sta DFLTO +: lda messages,x + beq :+ + jsr BSOUT + .if PLATFORM = diskio::platform::COMMODORE_16 + lda #BLINK + ora COLOR + sta COLOR + .endif; PLATFORM = diskio::platform::COMMODORE_16 + inx + bne :- +: pla + sta COLOR + pla + sta DFLTO + lda #8 +: dex + bne :- + dey + bne :- + sbc #1 + bne :- +.if PLATFORM = diskio::platform::COMMODORE_16 + pla + sta PALETTE + pla + sta TED_SCREEN_ADDR + pla + sta TED_CHARGEN_ADDR + pla + sta TED_BITMAP_ADDR + pla + sta TED_CTRL2 + pla + sta TED_CTRL1 +.else; PLATFORM <> diskio::platform::COMMODORE_16 + pla + sta CIA2_PRA + pla + .if PLATFORM = diskio::platform::COMMODORE_128 + sta VM1 + .endif ; PLATFORM = diskio::platform::COMMODORE_128 + sta VIC2_ADDR + pla + sta VIC2_CTRL2 + pla + sta VIC2_CTRL1 + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #FG_BG + sta VDC_CR +: bit VDC_SR; wait until update ready + bpl :- + pla + sta VDC_DR + .endif ; PLATFORM = diskio::platform::COMMODORE_128 +.endif; PLATFORM <> diskio::platform::COMMODORE_16 + pla + sta BGCOLOUR + pla + sta BORDERCOLOUR + rts + +drvrelistn: jsr UNLSN +drvlistn: lda FA + jsr LISTN +.if PLATFORM = diskio::platform::COMMODORE_128 + lda SERIAL + sta burstflag +.endif + lda #SA_OPENCHANNEL | COMMAND_ERROR_CHANNEL + jmp SECND + +memreadbyt: sta drvchkmr + 3 + stx drvchkmr + 4 + lda #drvchkmred - drvchkmr + ldx #.lobyte(drvchkmr) + ldy #.hibyte(drvchkmr) + jsr SETNAM + lda #COMMAND_ERROR_CHANNEL + ldx FA + tay + jsr SETLFS + jsr OPEN + bcc :+ + lda #0 + tax + rts + +: ldx #COMMAND_ERROR_CHANNEL + jsr CHKIN + jsr BASIN + pha + jsr BASIN + pha + lda #COMMAND_ERROR_CHANNEL + jsr CLOSE + jsr CLRCH + pla + tax + pla + clc + rts + +drvchkmr: .byte "m-r", $00, $00, 2 +drvchkmred: + +.if ONLY_1541_AND_COMPATIBLE = 0 + +getmodel: ; check if running on a 1541/70/71 compatible drive + lda #.lobyte($e5c6) + ldx #.hibyte($e5c6) + jsr memreadbyt + cmp #'4' + bne not1541 + + ; find out if 1541, 1541-C or 1541-II + lda #.lobyte($c002) + ldx #.hibyte($c002) + jsr memreadbyt + cmp #'c' + beq chk1541ii; branch if 'c' at $c002 (from 'COPYRIGHT' etc.) + ; find out if 1541 or 1541-C + lda #.lobyte($eaa3) + ldx #.hibyte($eaa3) + jsr memreadbyt + ldy #diskio::drivetype::DRIVE_1541 + cmp #$ff + beq check1541u + ; 1541-C: no $ff at $eaa3 (but likely $fe, data direction for track 0 sensor bit) + ldy #diskio::drivetype::DRIVE_1541_C + beq check1541u +chk1541ii: lda #.lobyte($e5b7) + ldx #.hibyte($e5b7) + jsr memreadbyt + tax + lda #$ff + cpx #'c' | $80; 'CBM DOS' etc. + bne :+; treat as 1541-II if no match, so as not to detect JiffyDOS, SpeedDOS etc. as 1541-C but 1541-II instead + ; find out if 1541-C or 1541-II + lda #.lobyte($eaa3) + ldx #.hibyte($eaa3) + jsr memreadbyt +: ldy #diskio::drivetype::DRIVE_1541_C + cmp #$ff + bne check1541u; 1541-C: no $ff at $eaa3 (but likely $fe, data direction for track 0 sensor bit) + iny; diskio::drivetype::DRIVE_1541_II: $ff at $eaa3 + ; check for discrete drive logics vs gate array + sty model1541 + jsr drvlistn + ldx #0 +: lda dchk1541ii,x + jsr CIOUT + inx + cpx #dch1541iie - dchk1541ii + bne :- + jsr UNLSN + lda #.lobyte($0300) + ldx #.hibyte($0300) + jsr memreadbyt + beq :+; branch if 1541-II + ldy #diskio::drivetype::DRIVE_1541 +check1541u: sty model1541 +: jsr drvlistn + ldx #0 +: lda drvch1541u,x + jsr CIOUT + inx + cpx #drvchkued - drvch1541u + bne :- + jsr UNLSN + lda #.lobyte($0300) + ldx #.hibyte($0300) + jsr memreadbyt + bmi :+; branch if 1541U +model1541 = * + 1 + ldy #0 + SKIPWORD +: ldy #diskio::drivetype::DRIVE_1541U + rts + +not1541: cmp #'7' + bne not157x + + ; find out if 1570 or 1571 + cpx #'1' | $80; 71 + ldy #diskio::drivetype::DRIVE_1570 + bcc is1570 + ; 1571 or 1571CR + + lda #.lobyte($e5c2) + ldx #.hibyte($e5c2) + jsr memreadbyt + cmp #'1'; 3.1 + ldy #diskio::drivetype::DRIVE_1571 + bcc :+ + iny; diskio::drivetype::DRIVE_1571CR +: +is1570: rts + +not157x: ; neither 1541 nor 157x + + ; try FD2000/FD4000 + lda #.lobyte(FD_F_HD_H) + ldx #.hibyte(FD_F_HD_H) + jsr memreadbyt + cmp #'f' + beq :+ + cmp #'h' + bne check1581 + ldy #diskio::drivetype::DRIVE_CMD_HD + rts + +: lda #.lobyte($fef0) + ldx #.hibyte($fef0) + jsr memreadbyt + ldy #diskio::drivetype::DRIVE_CMD_FD_2000 + cmp #'4' + bne isfd2000 + iny; diskio::drivetype::DRIVE_CMD_FD_4000 +isfd2000: rts + + ; check if 1581 +check1581: lda #.lobyte(CBM1581_8) + ldx #.hibyte(CBM1581_8) + jsr memreadbyt + ldy #diskio::drivetype::DRIVE_1581 + cmp #'8' + bne :+ + lda #diskio::status::DEVICE_INCOMPATIBLE +: rts + +dchk1541ii: .byte "m-e", .lobyte($0205), .hibyte($0205) + sei + lda $1c0c + ldx #$ec; disable byte sync: clear SOE + stx $1c0c + ldy #$01 + bit $1c01 + ldx $1c01 +: cpx $1c01; with disabled byte sync, $1c01 does not change on a 1541-II + bne :+ + iny + bne :- +: sty $0300; if not 0, not a 1541-II (discrete drive logics rather than gate array) + sta $1c0c + cli + rts +dch1541iie: + .assert (* - dchk1541ii) <= 41, error, "dchk1541ii too big" + +.endif ; ONLY_1541_AND_COMPATIBLE = 0 + + ; may be executed on 1570/71 with ONLY_1541_AND_COMPATIBLE +drvch1541u: .byte "m-e", .lobyte($0205), .hibyte($0205) + sei + ;ldy #0 + dey + sty $0300 + sty $1803; set all port pins as outputs + lda #$a4; bit 0 may be forced to GND (1541-II) or connected to track 0 sensor (1541-C, normally 0 = not on track 0) + sta $1801 + cmp $1801 + bne is1541u + anc #$8a; and #imm, but no asl/rol, bit 7 of result goes to carry + beq is1541u + bcc is1541u + tya + arr #$7f; bit 6 of result goes to carry + ror $0300 +is1541u: lda #$66; 1570/71 data directions + sta $1803 + ; no cli + rts +drvchkued: + .assert (* - drvch1541u) <= 41, error, "drvch1541u too big" + +drvmuteme: .byte "m-e", .lobyte($020b), .hibyte($020b), "krill." + sei + jmp $0300 +drvmutemed: +drvsilence: .byte "m-w", .lobyte($0300), .hibyte($0300), drvsilnced - (* + 1) + cli + ldy #$7f + sty $180e; no IRQs from VIA1 + sty $1c0e; no IRQs from VIA2 + sty $400d; no IRQs from CIA/MOS5710 + sei + sty $1802; set only ATN_IN as input + lda #$ff + sta $1803; set all $1801 port pins as outputs + ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low + stx $1801 + ldy #$d0 ; JOBCODE_EXECUTE + jmp $043b; waitactive +drvsilnced: + .assert (* - drvsilence) <= 41, error, "drvsilence too big" + +.if ONLY_1541_AND_COMPATIBLE = 0 + +drvslnc71: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnc71e - (* + 1) + cli + ldy #$7f + sty $180e; no IRQs from VIA1 + sty $1c0e; no IRQs from VIA2 + sty $400d; no IRQs from CIA/MOS5710 + sei + sty $1802; set only ATN_IN as input + lda #$20 ; 2 MHz mode + ora $1801 + sta $1801 + lda #$ff + ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low + ldy #$d0 ; JOBCODE_EXECUTE + jmp $043b; waitactive +drvslnc71e: + .assert (* - drvslnc71) <= 41, error, "drvslnc71 too big" + +.endif ; ONLY_1541_AND_COMPATIBLE = 0 + +drvsilencc: .byte "m-w", .lobyte($043b), .hibyte($043b), atnfallbck - (* + 1) +waitactive:;lda #$ff + sta $1c06; VIA2 timer 1 latch lo + jsr $0407; waitactpr + ldy #$90 ; ATNA_OUT set, CLK_OUT low, DATA_OUT low +: ;ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low + stx $1800; clear ATNA_OUT, set CLK_IN +: bit $1800 + bpl :- + ;ldy #$90 ; ATNA_OUT set, CLK_OUT low, DATA_OUT low + sty $1800; set ATNA_OUT, clear CLK_IN + ;lda #$ff + sta $1c05; timer 1 hi +: bit $1800 + bpl :--- + bit $1c05; timer 1 hi + bne :- + cmp $1803 + + .assert (* - drvsilencc) <= 41, error, "waitactive too big" + +atnfallbck: .byte "m-w", .lobyte($043b + atnfallbck - waitactive), .hibyte($043b + atnfallbck - waitactive), atnlo - (* + 1) + bne atnloop + cpx $1801; check if fast silencing using jmp ($1800) is possible + bne :+ + cpy $1800 + beq * + 32; silncentry: jmp ($1800) + ; slower fallback silence routine, this is required on older 1541U firmware revisions at least, which do not implement VIA1 port A correctly, + ; it may also be required for drives modded with a parallel connection, + ; this is executed unconditionally on 1570/71/CR at 2 MHz +: inc $1803; set all $1801 port pins to input +atnloop: bit $1800 + bmi atnloop + stx $1800; clear ATNA_OUT + ;lda #$ff + sta $1c05; timer 1 hi + ENABLE_WATCHDOG +: bit $1800 + bpl :- + sty $1800; set ATNA_OUT + + .assert (* - atnfallbck) <= 41, error, "atnfallbck too big" + +atnlo: .byte "m-w", .lobyte($0400), .hibyte($0400), atnhi - (* + 1) + jmp ($fffc) + .byte 0 + jmp ($1800); $0404, $04 = CLK_IN, jump to $0404 or $0484 +waitactpr: ;ldy #$d0 ; JOBCODE_EXECUTE + sty $01 ; JOBCODE0400 + ldy #$c0 ; timer 1 IRQs from VIA2 + sty $1c0e + bne :+; jmp + ; $0410, $10 = ATNA_OUT + ; ATN_IN is clear, but ATNA_OUT is set: clear ATNA_OUT and reset timer + stx $1800; x = $04 = CLK_IN + sta $1c05; timer 1 hi + ENABLE_WATCHDOG + jmp ($1800); jump to $0404 or $0484 +: ldy #1 + sty $3f; JOBN, required on 1571 so code in the correct buffer ($0400) is executed upon interrupt + rts + + .assert (* - atnlo) <= 41, error, "atnlo too big" + +atnhi: .byte "m-w", .lobyte($0481), .hibyte($0481), atnhiend - (* + 1) + sei + bmi * - 21; jmp, atnloop + ; $0484, $84 = ATN_IN | CLK_IN + ; ATN_IN is set, but ATNA_OUT is cleared: set ATNA_OUT + sty $1800; y = $90 = ATN_IN | ATNA_OUT + sei +silncentry: jmp ($1800); jump to $0410 or $0490 + .byte 0, 0, 0, 0, 0 + jmp ($1800); $0490, $90 = ATN_IN | ATNA_OUT, jump to $0410 or $0490 +atnhiend: + .assert (* - atnhi) <= 41, error, "atnhi too big" + +.if ONLY_1541_AND_COMPATIBLE = 0 + +drvslnc81: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnc81e - drvmute81 +drvmute81: ;ldy #0 + sty $4001 + dey + sty $4007 + lda #$19 +slnc81loop: sta $400f +: bit $4001 +slnc81sens: bpl slnc81loop; first, wait for a long period of ATN_IN set + ldx $4007 + bne :- + ldx #OPC_BMI + cpx slnc81sens - drvmute81 + $0300 + stx slnc81sens - drvmute81 + $0300 + .if DISABLE_WATCHDOG + jmp slnc81loop + .else + bne slnc81loop + jmp ($fffc) + .endif +drvslnc81e: + .assert (* - drvslnc81) <= 41, error, "drvslnc81 too big" + +drvslncfd: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslncfde - drvmutefd +drvmutefd: ldx #0 + stx $4001 + dex +slncfdloop: stx $4005 +: bit $4001 +slncfdsens: bpl slncfdloop + lda $4005 + bne :- + lda #OPC_BMI + cmp slncfdsens - drvmutefd + $0300 + sta slncfdsens - drvmutefd + $0300 + .if DISABLE_WATCHDOG + jmp slncfdloop + .else + bne slncfdloop + jmp ($fffc) + .endif +drvslncfde: + .assert (* - drvslncfd) <= 41, error, "drvslncfd too big" + +drvslnchd: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnchde - drvmutehd +drvmutehd: ldx #0 + stx $8000 + dex +slnchdloop: stx $8005 +: bit $8000 +slnchdsens: bpl slnchdloop + lda $8005 + bne :- + lda #OPC_BMI + cmp slnchdsens - drvmutehd + $0300 + sta slnchdsens - drvmutehd + $0300 + .if DISABLE_WATCHDOG + jmp slnchdloop + .else + bne slnchdloop + jmp ($fffc) + .endif +drvslnchde: + .assert (* - drvslnchd) <= 41, error, "drvslnchd too big" + +dcodeselt0: .byte .lobyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41) + .byte .lobyte(cbm1571::drvcodeend71 - cbm1571::drvcodebeg71 + cbm1571::drivecode71) + .byte .lobyte(cbm1581::drvcodeend81 - cbm1581::drvcodebeg81 + cbm1581::drivecode81) +dcodeselt1: .byte .hibyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41) + .byte .hibyte(cbm1571::drvcodeend71 - cbm1571::drvcodebeg71 + cbm1571::drivecode71) + .byte .hibyte(cbm1581::drvcodeend81 - cbm1581::drvcodebeg81 + cbm1581::drivecode81) +dcodeselt2: .byte .lobyte(cbm1541::drvprgend41 - cbm1541::drvcodeend41 + cbm1541::TRAMPOLINEOFFSET) + .byte .lobyte(cbm1571::drvprgend71 - cbm1571::drvcodeend71 + cbm1571::TRAMPOLINEOFFSET) + .byte .lobyte(cbm1581::drvprgend81 - cbm1581::drvcodeend81) +dcodeselt3: .byte .lobyte(cbm1541::drivecode41) + .byte .lobyte(cbm1571::drivecode71) + .byte .lobyte(cbm1581::drivecode81) +dcodeselt4: .byte .hibyte(cbm1541::drivecode41) + .byte .hibyte(cbm1571::drivecode71) + .byte .hibyte(cbm1581::drivecode81) +dcodeselt5: .byte .hibyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET) + .byte .hibyte(cbm1571::drvcodeend71 - cbm1571::TRAMPOLINEOFFSET) + .byte .hibyte(cbm1581::drvcodeend81) +dcodeselt6: .byte .lobyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET) + .byte .lobyte(cbm1571::drvcodeend71 - cbm1571::TRAMPOLINEOFFSET) + .byte .lobyte(cbm1581::drvcodeend81) +dcodeselt7: .byte .hibyte(cbm1541::dinstall) + .byte .hibyte(cbm1571::dinstall) + .byte .hibyte(cbm1581::dinstall) +dcodeselt8: .byte .lobyte(cbm1541::dinstall) + .byte .lobyte(cbm1571::dinstall) + .byte .lobyte(cbm1581::dinstall) +dcodeselt9: .byte 41 + .byte 71 + .byte 81 +dcodeselta: .byte DIRTRACK + .byte DIRTRACK + .byte DIRTRACK81 + +.endif; ONLY_1541_AND_COMPATIBLE = 0 + + ; check if drive allows code upload and execution +chkdrvcode: lda #.lobyte($0300) + ldx #.hibyte($0300) + jsr memreadbyt + bcc :+ + lda #$ff + rts +: eor #$ff + sta drvchkval + 1 + jsr drvlistn + ldx #0 +: lda drvchkme,x + jsr CIOUT + inx + cpx #drvchkmed - drvchkme + bne :- + jsr UNLSN + lda #.lobyte($0300) + ldx #.hibyte($0300) + jsr memreadbyt +drvchkval: cmp #0 + rts + +drvchkme: .byte "m-e", .lobyte($020a), .hibyte($020a), "krill" + lda #$ff + eor $0300 + sta $0300 + rts +drvchkmed: + .assert (* - drvchkme) <= 41, error, "drvchkme too big" + +.if LOAD_VIA_KERNAL_FALLBACK +drvfaststp: .byte MINSTEPSPEED, $01, .hibyte($1c07), .lobyte($1c07), "w-m"; read backward +twosided: .byte "1m>0u"; read backward, enable 1571 mode to read two-sided disks +.endif; LOAD_VIA_KERNAL_FALLBACK + +dcodesel5 = * + 1 +dcodesel6 = * + 2 +drvrutmw: .byte 35, 0, 0, "w-m"; read backward +droutrun: .byte "m-e", .lobyte($0209), .hibyte($0209), "krill" +dcodesel7 = * + 1 +dcodesel8 = * + .byte $00, $00, .lobyte(REPOSITORY_VERSION), .hibyte(REPOSITORY_VERSION), PLATFORM +family: .byte 41 +dirtrack: .byte DIRTRACK, FILENAME_MAXLENGTH, CONFIG_INTERNAL +droutruned: + +messages: +.if ONLY_1541_AND_COMPATIBLE = 0 +errormute: .byte PETSCII_LO_UP_CASE +.if PLATFORM = diskio::platform::COMMODORE_128 + .byte PETSCII_BLINK +.endif +.if PLATFORM = diskio::platform::COMMODORE_16 + .byte PETSCII_ORANGE +.else + .byte PETSCII_WHITE +.endif + .byte "ERROR: Failed to mute device #" +mutedevice: .byte "00.", 0 +.endif; ONLY_1541_AND_COMPATIBLE = 0 + +warnemubug: .byte PETSCII_LO_UP_CASE +.if PLATFORM = diskio::platform::COMMODORE_128 + .byte PETSCII_BLINK +.endif +.if PLATFORM = diskio::platform::COMMODORE_16 + .byte PETSCII_ORANGE +.else + .byte PETSCII_WHITE +.endif + .byte "WARNING: Buggy " +.if PLATFORM = diskio::platform::COMMODORE_64 + .byte "1541U firmware" +.else + .byte "emulator" +.endif + .byte " detected. Please update.", 0 + +version: .byte "Krill's Loader, revision ", REPOSITORY_VERSION_STRING, PETSCII_RETURN, "cfg " + itoa4 MIN_DEVICE_NO + itoa8 MAX_DEVICE_NO + itoa1 ONLY_1541_AND_COMPATIBLE + .byte '.' + itoa8 DIRTRACK + itoa8 DIRTRACK81 + itoa8 FILENAME_MAXLENGTH + .byte '.' + itoa8 MINSTEPSPEED + itoa8 MAXSTEPSPEED + itoa4 STEPPERACC + itoa4 SINGLESTEPSPEED + .byte '.' + itoa4 (2 * LOAD_COMPD_API) + LOAD_RAW_API + itoa1 NTSC_COMPATIBILITY + itoa1 PREFER_SPEED_OVER_SIZE + itoa1 UNINSTALL_API + itoa1 FILE_EXISTS_API + itoa1 LOAD_UNDER_D000_DFFF + itoa1 ALLOW_2_MHZ_ON_C128 + itoa4 (2 * MEM_DECOMP_TO_API) + MEM_DECOMP_API + itoa1 (2 * LOAD_TO_API) + END_ADDRESS_API + itoa1 LOAD_VIA_KERNAL_FALLBACK + itoa1 CLOSE_FILE_API + itoa8 CONFIG_INTERNAL + .byte '.' + itoa4 DECOMPRESSOR + .byte 0 + + CHECK_INSTALL_END_ADDRESS + +.exportzp status_OK = diskio::status::OK +.exportzp status_DEVICE_INCOMPATIBLE = diskio::status::DEVICE_INCOMPATIBLE +.exportzp status_TOO_MANY_DEVICES = diskio::status::TOO_MANY_DEVICES +.exportzp status_GENERIC_KERNAL_ERROR = diskio::status::GENERIC_KERNAL_ERROR +.exportzp status_DEVICE_NOT_PRESENT = diskio::status::DEVICE_NOT_PRESENT +.exportzp status_FILE_NOT_FOUND = diskio::status::FILE_NOT_FOUND + +.if PLATFORM <> diskio::platform::COMMODORE_16 + .exportzp config_ALLOW_2_MHZ_ON_C128 = ALLOW_2_MHZ_ON_C128 +.endif +.exportzp config_DECOMPRESSOR = DECOMPRESSOR +.exportzp config_DIRTRACK = DIRTRACK +.exportzp config_DIRTRACK81 = DIRTRACK81 +.exportzp config_END_ADDRESS_API = END_ADDRESS_API +.exportzp config_FILENAME_MAXLENGTH = FILENAME_MAXLENGTH +.exportzp config_FILE_EXISTS_API = FILE_EXISTS_API +.exportzp config_INTERNAL = CONFIG_INTERNAL +.exportzp config_LOAD_COMPD_API = LOAD_COMPD_API +.exportzp config_LOAD_RAW_API = LOAD_RAW_API +.exportzp config_LOAD_TO_API = LOAD_TO_API +.exportzp config_LOAD_UNDER_D000_DFFF = LOAD_UNDER_D000_DFFF +.exportzp config_LOAD_VIA_KERNAL_FALLBACK = LOAD_VIA_KERNAL_FALLBACK +.exportzp config_MEM_DECOMP_API = MEM_DECOMP_API +.exportzp config_MEM_DECOMP_TO_API = MEM_DECOMP_TO_API +.exportzp config_NTSC_COMPATIBILITY = NTSC_COMPATIBILITY +.exportzp config_ONLY_1541_AND_COMPATIBLE = ONLY_1541_AND_COMPATIBLE +.exportzp config_PREFER_SPEED_OVER_SIZE = PREFER_SPEED_OVER_SIZE +.exportzp config_UNINSTALL_API = UNINSTALL_API diff --git a/loader/src/make-linkfile.pl b/loader/src/make-linkfile.pl new file mode 100644 index 0000000..c159626 --- /dev/null +++ b/loader/src/make-linkfile.pl @@ -0,0 +1,32 @@ +#!/usr/bin/env perl + +my $platform = $ARGV[0]; + +my $zp = hex($ARGV[1]); +my $install = hex($ARGV[2]); +my $resident = hex($ARGV[3]); +my $transient = hex($ARGV[4]); + +printf ("MEMORY +{ + ZPRAM: start = \$%x, size = \$%x; + ZPRAM2: start = \$%x, size = \$%x; + INSTALLRAM: start = \$%x, size = \$%x, file = \"../build/install-$platform.prg\"; + RESIDENTRAM: start = \$%x, size = \$%x, file = \"../build/loader-$platform.prg\"; + TRANSIENTRAM: start = \$%x, size = \$%x, file = \"../build/transient-$platform.prg\"; +} + +SEGMENTS +{ + DISKIO_ZP: load = ZPRAM, type = zp; + DISKIO_PLUGIN_ZP: load = ZPRAM2, type = zp, optional = yes; + DISKIO: load = RESIDENTRAM; + DISKIO_PLUGIN: load = TRANSIENTRAM, optional = yes; + DISKIO_INSTALL: load = INSTALLRAM; +} +", +$zp, 0x0100 - $zp, +$zp, 0x0100 - $zp, +$install - 2, 0x10002 - $install, +$resident - 2, 0x10002 - $resident, +$transient - 2, 0x10002 - $transient); diff --git a/loader/src/make-loadersymbolsinc.pl b/loader/src/make-loadersymbolsinc.pl new file mode 100755 index 0000000..5a335b1 --- /dev/null +++ b/loader/src/make-loadersymbolsinc.pl @@ -0,0 +1,272 @@ +#!/usr/bin/env perl + +=head1 NAME + +make-loadersymbolsinc.pl + +=head1 DESCRIPTION + +This script extracts relevant symbols from a ld65-generated map file. +The resulting include file can be included in third-party assembly source code. + +=head1 SYNOPSIS + + make-loadersymbolsinc.pl loader-c64.map > loadersymbols-c64.inc + +=cut + +use strict; +use warnings; + +use English qw( -no_match_vars ); # OS_ERROR etc. + + +main(); + +sub main +{ + my $version = $ARGV[0]; + my $project = $ARGV[1]; + my @input = read_file($ARGV[2]); + my ($diskio_segments, $symbols) = extract_all_symbols(@input); + print_symbols($version, $project, $diskio_segments, filter_symbols($diskio_segments, $symbols)); + + return; +} + +sub read_file +{ + my ($filename) = @ARG; + + open(my $fh, '<', $filename) + or die "cannot read file '$filename': $OS_ERROR"; + + my @input = <$fh>; + close $fh; + + return @input; +} + +sub extract_all_symbols +{ + my @input = @ARG; + + my $current_list; + my %diskio_segments; + my %symbols; + foreach my $i (@input) { + + if ($i =~ qr{list.*?:}) { + $current_list = $i; + + } elsif ($current_list =~ qr{Segment}) { + # only check against the DISKIO segments + if ($i =~ qr{(DISKIO\w*)\s+(\w+)\s+(\w+)}) { + my $label = lc $1; + $diskio_segments{$label . '_start'} = hex '0x' . $2; + $diskio_segments{$label . '_end'} = hex '0x' . $3; + } + + } elsif ($current_list =~ qr{Exports}) { + # the exported symbols will be filtered by the DISKIO segments + if ($i =~ qr{(\w+)\s+(\w+)\s+\w+\s+(\w+)\s+(\w+)}) { + # double-entry line + $symbols{$1} = hex '0x' . $2; + $symbols{$3} = hex '0x' . $4; + } elsif ($i =~ qr{(\w+)\s+(\w+)\s+\w+}) { + # single-entry line + $symbols{$1} = hex '0x' . $2; + } + } + } + + return \%diskio_segments, \%symbols; +} + +sub filter_symbols +{ + my ($diskio_segments, $symbols) = @ARG; + + my %filtered_host; + my %filtered_drive; + my $decompressor; + while (my ($name, $address) = each %$symbols) { + # do not regard *fix/*fix1 symbols, all-caps or *1 (41/71/81) symbols + if ($address && ($name !~ /fix|fix1|[A-Z]|1$/)) { + # only symbols in the DISKIO segments are printed + if ((($address >= $diskio_segments->{diskio_start}) + && ($address <= $diskio_segments->{diskio_end})) + + || (exists($diskio_segments->{diskio_plugin_start}) + && ($address >= $diskio_segments->{diskio_plugin_start}) + && ($address <= $diskio_segments->{diskio_plugin_end})) + + || (($address >= $diskio_segments->{diskio_install_start}) + && ($address <= $diskio_segments->{diskio_install_end})) + + || (($address >= $diskio_segments->{diskio_zp_start}) + && ($address <= $diskio_segments->{diskio_zp_end})) + + || (exists($diskio_segments->{diskio_plugin_zp_start}) + && ($address >= $diskio_segments->{diskio_plugin_zp_start}) + && ($address <= $diskio_segments->{diskio_plugin_zp_end}))) + { + $filtered_host{$name} = $address; + } + } elsif (($name =~ /config_/) || ($name =~ /status_/)) { + $filtered_host{$name} = $address; + } elsif (($name =~ /1$/) && ($name !~ /fix1$/)) { + $filtered_drive{$name} = $address; + } elsif ($name =~ /DECOMPRESSOR_(\S+)/) { + $decompressor = $1; + } + } + + return \%filtered_host, \%filtered_drive, $decompressor; +} + +sub print_symbols +{ + my ($version, $project, $diskio_segments, $host_symbols, $drive_symbols, $decompressor) = @ARG; + + my @host_symbols_sorted_by_address = + sort { $host_symbols->{$a} <=> $host_symbols->{$b} } keys %{$host_symbols}; + + my @drive_symbols_sorted_by_address = + sort { $drive_symbols->{$a} <=> $drive_symbols->{$b} } keys %{$drive_symbols}; + + my $target = ($diskio_segments->{diskio_plugin_start}) ? "customdrivecode" : "prg"; + for (@host_symbols_sorted_by_address) { + if("save" eq $_ ) { + $target = "save"; + last; + } + } + + chomp(my $datetime = `date -R`); + print "; repository version " . $version . ", built on " . $datetime . " for project \"" . $project . "\" using\n"; + print "; make PLATFORM=" . $ENV{'_PLATFORM_'} . " " . $target . " INSTALL=" . $ENV{'INSTALL'} . " RESIDENT=" . $ENV{'RESIDENT'}; + unless ($target =~ /prg/) { + print " TRANSIENT=" . $ENV{'TRANSIENT'} + } + print " ZP=" . $ENV{'ZP'} . " PROJECT=" . $ENV{'PROJECT'} . "\n\n"; + + print "; configuration\n"; + foreach my $name (sort (keys(%{$host_symbols}))) { + if ($name =~ /config_/) { + if ($name =~ /config_DECOMPRESSOR/) { + print_symbol($name, $host_symbols->{$name}, $decompressor); + } else { + print_symbol($name, $host_symbols->{$name}); + } + } + } + + print "\n; status codes\n"; + foreach my $name (@host_symbols_sorted_by_address) { + if ($name =~ /status_/) { + print_hex_symbol($name, $host_symbols->{$name}); + } + } + + my $zp_end = $diskio_segments->{diskio_zp_end}; + if (exists($diskio_segments->{diskio_plugin_zp_end})) { + $zp_end = $diskio_segments->{diskio_plugin_zp_end} > $diskio_segments->{diskio_zp_end} ? $diskio_segments->{diskio_plugin_zp_end} : $diskio_segments->{diskio_zp_end}; + } + + print "\n; zeropage "; + printf '$%.2x-$%.2x', $diskio_segments->{diskio_zp_start}, $zp_end + 1; + print "\n"; + print_hex_symbol("loader_zp_first", $host_symbols->{"loader_zp_first"}); + print_segment_symbols($host_symbols, $diskio_segments->{diskio_zp_start}, $zp_end, @host_symbols_sorted_by_address); + + print "\n; install "; + printf '$%.4x-$%.4x', $diskio_segments->{diskio_install_start} + 2, $diskio_segments->{diskio_install_end} + 1; + print "\n"; + # only one relevant symbol ("install"), stop after that so that other symbols from overlaying segments (transient) will be printed there instead + print_segment_symbols($host_symbols, $diskio_segments->{diskio_install_start} + 2, $diskio_segments->{diskio_install_start} + 2, @host_symbols_sorted_by_address); + + print "\n; resident "; + printf '$%.4x-$%.4x', $diskio_segments->{diskio_start} + 2, $diskio_segments->{diskio_end} + 1; + print "\n"; + print_segment_symbols($host_symbols, $diskio_segments->{diskio_start} + 2, $diskio_segments->{diskio_end}, @host_symbols_sorted_by_address); + + unless ($target =~ /prg/) { + if ($diskio_segments->{diskio_plugin_start}) { + print "\n; transient "; + printf '$%.4x-$%.4x', $diskio_segments->{diskio_plugin_start} + 2, $diskio_segments->{diskio_plugin_end} + 1; + print "\n"; + print_segment_symbols($host_symbols, $diskio_segments->{diskio_plugin_start} + 2, $diskio_segments->{diskio_plugin_end}, @host_symbols_sorted_by_address); + } + + unless ($target =~ /save/) { + print "\n; drive\n"; + print_drive_symbols($drive_symbols, "41", @drive_symbols_sorted_by_address); + print "\n"; + print_drive_symbols($drive_symbols, "71", @drive_symbols_sorted_by_address); + print "\n"; + print_drive_symbols($drive_symbols, "81", @drive_symbols_sorted_by_address); + } + } + + return; +} + +sub print_segment_symbols +{ + my ($symbols, $start, $end, @symbols_sorted_by_address) = @ARG; + + foreach my $name (@symbols_sorted_by_address) { + if ($name =~ /config_|status_|loader_zp_first/) { + next; + } + + my $address = $symbols->{$name}; + if ($address == 0) { + next; + } + + if (($address >= $start) + && ($address <= $end)) { + print_hex_symbol($name, $symbols->{$name}); + + $symbols->{$name} = 0; + } + } + + return; +} + +sub print_drive_symbols +{ + my ($symbols, $filter, @symbols_sorted_by_address) = @ARG; + + foreach my $name (@symbols_sorted_by_address) { + if ($name =~ /$filter$/) { + print_hex_symbol($name, $symbols->{$name}); + } + } + + return; +} + +sub print_symbol +{ + my ($name, $address) = @ARG; + + my $format = "%-31s = %d%s\n"; + printf $format, $name, $address, $ARG[2] ? "; " . $ARG[2] : ""; + + return; +} + +sub print_hex_symbol +{ + my ($name, $address) = @ARG; + + my $address_length = ($address < 256) ? '2' : '4'; + my $format = '%-31s = $%.' . $address_length . "x\n"; + printf $format, $name, $address; + + return; +} diff --git a/loader/src/resident.s b/loader/src/resident.s new file mode 100755 index 0000000..b0a014a --- /dev/null +++ b/loader/src/resident.s @@ -0,0 +1,1408 @@ + +.fileopt comment, "Loader resident code portion" +.fileopt compiler, "CA65" +.fileopt author, "Gunnar Ruthenberg" + +__NO_LOADER_SYMBOLS_IMPORT = 1 +__NOIMPORTVARS = 1 + +.include "loader.inc" +.include "kernal.inc" + +.include "cpu.inc" + +.include "hal/hal.inc" + + +HAVE_DECOMPRESSOR = DECOMPRESSOR <> DECOMPRESSORS::NONE +LOAD_TO_RAM_UNDER_IO = LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16) +HAVE_ENDGETBLOCK = (PLATFORM <> diskio::platform::COMMODORE_64) | USE_2_MHZ +LOADCOMPD_TO = LOAD_COMPD_API & LOAD_TO_API +OPENFILE_POLLBLOCK = DISABLE_WATCHDOG | .defined(OPENFILE_POLLBLOCK_API) + +.if LOAD_COMPD_API & (!HAVE_DECOMPRESSOR) + .error "***** Option LOAD_COMPD_API requires DECOMPRESSOR != DECOMPRESSORS::NONE *****" +.endif + +.if MEM_DECOMP_API & (!HAVE_DECOMPRESSOR) + .error "***** Option MEM_DECOMP_API requires DECOMPRESSOR != DECOMPRESSORS::NONE *****" +.endif + +.if MEM_DECOMP_TO_API & (!MEM_DECOMP_API) + .error "***** Option MEM_DECOMP_TO_API requires MEM_DECOMP_API *****" +.endif + +.if HAVE_DECOMPRESSOR & (!LOAD_COMPD_API) & (!MEM_DECOMP_API) + .error "***** Decompressor included but not used, as neither LOAD_COMPD_API nor MEM_DECOMP_API are enabled *****" +.endif + +.if (!LOAD_RAW_API) & (!LOAD_COMPD_API) + .error "***** No actual loading calls enabled, please select LOAD_RAW_API and/or LOAD_COMPD_API *****" +.endif + +.if (FILENAME_MAXLENGTH < 1) | (FILENAME_MAXLENGTH > 16) + .error "***** Invalid FILENAME_MAXLENGTH setting, please set it to 1..16 *****" +.endif + + +.segment "DISKIO_ZP" : zeropage + +; all zeropage variables can be +; overwritten when the loader is idle + +.macro alloc_zpvar symbol +symbol: .res 1 + .exportzp symbol +.endmacro + +.macro alloc_zpvar_2 symbol +symbol: .res 2 + .exportzp symbol +.endmacro + +loader_zp_first = * +.export loader_zp_first + +alloc_zpvar loadaddrlo +alloc_zpvar loadaddrhi + +.if LOADCOMPD_TO + alloc_zpvar loadaddroffslo + alloc_zpvar loadaddroffshi +.endif + +.if HAVE_DECOMPRESSOR + .if MEM_DECOMP_TO_API + alloc_zpvar decdestlo + alloc_zpvar decdesthi + .else +decdestlo: .res 1 +decdesthi: .res 1 + .endif + .if DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH + .res 2; dummy variables for init loop + .elseif DECOMPRESSOR = DECOMPRESSORS::ZX0 + .res 1; dummy variable for init loop + .endif +.endif + +.if LOAD_VIA_KERNAL_FALLBACK | END_ADDRESS_API + alloc_zpvar endaddrlo + alloc_zpvar endaddrhi +.endif + +alloc_zpvar_2 BLOCKDESTLO +BLOCKINDEX = BLOCKDESTLO +.if HAVE_DECOMPRESSOR + alloc_zpvar BLOCKSIZE +.else + BLOCKSIZE = BLOCKINDEX + 1 +.endif +.if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO + alloc_zpvar STACKPOINTER +.endif +.if HAVE_DECOMPRESSOR + alloc_zpvar STREAMAVAIL +.endif + +.if PLATFORM <> diskio::platform::COMMODORE_16 + alloc_zpvar GETBYTE_CLOCK_ATN_HI +.endif + +.if LOAD_VIA_KERNAL_FALLBACK + alloc_zpvar KERNALBLKC +.endif + +DECOMPVARS: +.exportzp DECOMPVARS + +.if DECOMPRESSOR = DECOMPRESSORS::NONE + +.elseif DECOMPRESSOR = DECOMPRESSORS::BITNAX + NO_ZP_READ_PTR = 1 + POLLBLOCK_PRESERVE_X = 1 + .res 7 +.elseif DECOMPRESSOR = DECOMPRESSORS::BYTEBOOZER2 + NO_ZP_READ_PTR = 1 + POLLBLOCK_PRESERVE_X = 1 + .res 1 +.elseif DECOMPRESSOR = DECOMPRESSORS::DOYNAX_LZ + NO_ZP_READ_PTR = 1 + POLLBLOCK_PRESERVE_X = 1 + .res 3 +.elseif DECOMPRESSOR = DECOMPRESSORS::EXOMIZER + NO_BLOCK_INTERFACE = 1 + .res 9 +.elseif DECOMPRESSOR = DECOMPRESSORS::LEVELCRUSH + NO_BLOCK_INTERFACE = 1 + .res 2 +.elseif DECOMPRESSOR = DECOMPRESSORS::LZSA2 + .res 13 +.elseif DECOMPRESSOR = DECOMPRESSORS::NUCRUNCH + NO_ZP_READ_PTR = 1 + POLLBLOCK_PRESERVE_X = 1 + .res 5 +.elseif DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH + NO_BLOCK_INTERFACE = 1 + .res 3 +.elseif DECOMPRESSOR = DECOMPRESSORS::SUBSIZER + NO_BLOCK_INTERFACE = 1 + .res 8 +.elseif DECOMPRESSOR = DECOMPRESSORS::TINYCRUNCH + EXTRAREADAHEAD = 1 + .res 6 +.elseif DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH + .res 5 +.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0 + USE_DALI = 1 + .res 5 +.endif + +.ifndef POLLBLOCK_PRESERVE_X + POLLBLOCK_PRESERVE_X = 0 +.endif + +.ifdef NO_BLOCK_INTERFACE + HAVE_BLOCK_INTERFACE = 0 + NO_ZP_READ_PTR = 1 + decompsrc = getstrmbyt + 1 +.else + HAVE_BLOCK_INTERFACE = 1 +.endif + +.ifndef EXTRAREADAHEAD + EXTRAREADAHEAD = 0 +.endif + +; base read-ahead of 1 due to read 254/use 256 bytes STREAMAVAIL inaccuracy (plus 1 for >= ~128 blocks, see getblock) +BASEREADAHEAD = 1 +READAHEAD = BASEREADAHEAD + EXTRAREADAHEAD + +.ifdef NO_ZP_READ_PTR + ZP_READ_PTR = 0 +.else + ZP_READ_PTR = 1 +.endif + + +HAVE_GETBYTE = !HAVE_BLOCK_INTERFACE + +.macro SET_DECSRC_LO + .if LOAD_COMPD_API + .if ZP_READ_PTR + sta .lobyte(decompsrc) + .endif + .if HAVE_GETBYTE + sta decompsrc + .endif + .endif; LOAD_COMPD_API +.endmacro + +.macro SET_DECSRC_HI + .if LOAD_COMPD_API + .if ZP_READ_PTR + sta .lobyte(decompsrc + 1) + .else + sta decompsrc + 1 + .if DECOMPRESSOR = DECOMPRESSORS::ZX0 + .if READ2_OPT + sta lz_read_2 + 2 + .endif + sta lz_read_3 + 2 + .endif + .endif + .endif; LOAD_COMPD_API +.endmacro + + +loader_zp_last = * - 1 +.export loader_zp_last + + CHECK_LOADER_ZP_ADDRESSES + +.segment "DISKIO" + + +.if PLATFORM = diskio::platform::COMMODORE_16 +KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY = $0c +.else +KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY = $07 +.endif + + +.ifdef RESIADDR + .org RESIADDR - 2 + .word * + 2; load address +.endif + + CHECK_RESIDENT_START_ADDRESS + +.if LOAD_RAW_API + +.export loadraw + + ; --- load file without decompression --- + ; in: x/y - x: lo, y: hi to 0-terminated filename string, + ; zero-length file name will load next file + ; c - if LOAD_TO_API != 0, c = 0: load to address as stored in the file + ; c = 1: load to caller-specified address (loadaddrlo/hi) + ; out: c - set on error + ; a - status + + ; C-64/128: when LOAD_UNDER_D000_DFFF is non-0, this call assumes that the IO space at $d000 is enabled +loadraw: jsr openfile + .if LOAD_VIA_KERNAL_FALLBACK + bcs openerror; only with kernal fallback because only then the call might fail + .endif; LOAD_VIA_KERNAL_FALLBACK + + .if LOAD_TO_RAM_UNDER_IO + BUFFER_MEMCONFIG + .endif; LOAD_TO_RAM_UNDER_IO + + .if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO + tsx + stx STACKPOINTER + .endif; LOAD_COMPD_API +: + .if LOAD_VIA_KERNAL_FALLBACK & LOAD_COMPD_API + lda #0 + sta STREAMAVAIL + .endif; LOAD_VIA_KERNAL_FALLBACK & LOAD_COMPD_API + + jsr pollblock + bcc :- + +.if LOAD_TO_RAM_UNDER_IO + RESTORE_MEMCONFIG_Y +.endif + cmp #diskio::status::OK + 1 + .if LOAD_VIA_KERNAL_FALLBACK +openerror: + .endif; LOAD_VIA_KERNAL_FALLBACK + rts + +.endif; LOAD_RAW_API + +.if LOAD_COMPD_API + +.export loadcompd + .if DISABLE_WATCHDOG +.export loadcompdopen +.export loadcompdexecute + .endif; DISABLE_WATCHDOG + + ; --- load a compressed file --- + ; in: x/y - x: lo, y: hi to 0-terminated filename string, + ; zero-length file name will load next file + ; c - if LOAD_TO_API != 0, c = 0: load to address as stored in the file + ; c = 1: load with caller-specified address offset (loadaddroffslo/hi) + ; out: c - set on error + ; a - status + + ; C-64/128: when LOAD_UNDER_D000_DFFF is non-0, this call assumes that the IO space at $d000 is enabled +loadcompd: + .if LOAD_TO_API + bcs :+ + clc + jsr openfile + jmp compdfileopen +: + .endif; LOAD_TO_API + jsr loadcompdopen +compdfileopen: + .if LOAD_VIA_KERNAL_FALLBACK + ; only with kernal fallback because only then the call can fail + bcc :+ + rts +: + .endif; LOAD_VIA_KERNAL_FALLBACK + +loadcompdexecute: + + .if LOAD_TO_RAM_UNDER_IO + BUFFER_MEMCONFIG + .endif; LOAD_TO_RAM_UNDER_IO + + tsx + stx STACKPOINTER + + .if LOAD_TO_RAM_UNDER_IO + ENABLE_ALL_RAM + .endif; LOAD_TO_RAM_UNDER_IO + + jsr decompress + + .if LOAD_TO_RAM_UNDER_IO + ENABLE_IO_SPACE + .endif; LOAD_TO_RAM_UNDER_IO + + ; decompression is finished + + ; handle case that decompressing is as quick as loading, + ; this will fetch the loading finished status and ack loading, + ; and also loads any remaining uncompressed blob with in-place decompression +: jsr pollblock + bcc :- + + ; loading and decompression is done + + OK_CLC; a = #diskio::status::OK; $00, clc = all ok +.endif; !LOAD_COMPD_API + +pollfail: +.if LOAD_VIA_KERNAL_FALLBACK + stx :+ + 1; x holds KERNAL error code +.endif; LOAD_VIA_KERNAL_FALLBACK + +.if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO + ldx STACKPOINTER + txs +.endif; !LOAD_COMPD_API + +.if LOAD_TO_RAM_UNDER_IO + RESTORE_MEMCONFIG_Y +.endif + +.if LOAD_VIA_KERNAL_FALLBACK + php + tay + BRANCH_IF_NOT_INSTALLED :+ + plp +polldone: php + IDLE +: ldx #0; x holds KERNAL error code + tya + plp +.else; !LOAD_VIA_KERNAL_FALLBACK +polldone: IDLE +.endif; !LOAD_VIA_KERNAL_FALLBACK + +eofidle: +.if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH + ; return execution address in x/y + ldx lo + 1 + ldy hi + 1 +.endif; DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH +blknotrdy: +loaddone: rts + +POLL_RESTORE = LOAD_VIA_KERNAL_FALLBACK | HAVE_ENDGETBLOCK + +.if POLL_RESTORE + +pollfailr: tay + ENDGETBLOCK + IDLE + tya + jmp pollfail + +polldoner: tay + ENDGETBLOCK + tya + jmp polldone + +.endif; POLL_RESTORE + +.if OPENFILE_POLLBLOCK +.export pollblock +.endif; OPENFILE_POLLBLOCK + +pollblock: +.if LOAD_VIA_KERNAL_FALLBACK + BRANCH_IF_INSTALLED :+ + jmp kernalgblk +: +.endif; LOAD_VIA_KERNAL_FALLBACK + + POLL_BLOCK eofidle, blknotrdy, pollfail + SEND_BLOCK_SIGNAL + + lda #OPC_RTS; disable getblock store and loop + jsr getbyteswt; get next contiguous block index/block size or error/eof code + ;sec + tax + inx +.if POLL_RESTORE + beq pollfailr; branch if a = $ff = diskio::status::FILE_NOT_FOUND + tay + beq polldoner +.else; !POLL_RESTORE + beq pollfail; branch if a = $ff = diskio::status::FILE_NOT_FOUND + tay + beq polldone +.endif; !POLL_RESTORE + + dey + jsr get1byte; get block index and last block size/next contiguous block index flag + + cmp #$80; sign extension + ror + bcs :+; branch if file's last block +.if LOAD_COMPD_API + sty STREAMAVAIL +.endif; LOAD_COMPD_API + ldy #$01; block size +: clc + adc BLOCKINDEX + sta BLOCKINDEX + clc + bne calcaddr + + ; first block: get load address + + jsr get1byte; load address lo +.if LOADCOMPD_TO + clc + adc loadaddroffslo + php +.endif; LOADCOMPD_TO +storeladrl: sta loadaddrlo; is changed to lda on load_to + SET_DECSRC_LO + jsr get1byte; load address hi +.if LOADCOMPD_TO + plp + adc loadaddroffshi + sec +.endif; LOADCOMPD_TO +storeladrh: sta loadaddrhi; is changed to lda on load_to + SET_DECSRC_HI + ;sec + +calcaddr: ; calculate the position in memory according to the block index, + ; this is performing: pos = loadaddr + BLOCKINDEX * 254 - (BLOCKINDEX ? 2 : 0) + php + lda loadaddrlo + sbc BLOCKINDEX + pha + lda loadaddrhi + adc BLOCKINDEX + tax + pla + plp + sbc BLOCKINDEX + bcs :+ + dex +: clc + sty BLOCKSIZE + sbc BLOCKSIZE + sta storebyte + 1 + + ; getblock loop/get1byte subroutine + +.if LOAD_TO_RAM_UNDER_IO + ; trading off size for speed: choose either of two getblock loops + ; depending on the destination of the data to be downloaded + php + txa + sbc #1 + cmp #.hibyte($cf00) + bcc :+ + cmp #.hibyte($e000) + .if (PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE + bcs :+ + jmp getblockio + .else; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE) + bcc getblockio + .endif; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE) +: plp +.endif; LOAD_TO_RAM_UNDER_IO + + lda #OPC_STA_ABSY; enable getblock store and loop +getbyteswt: sta storebyte + txa + sbc #1 + sta storebyte + 2 + +.macro STORE +storebyte: sta $0000,y +.endmacro; STORE +get1byte: RECEIVE STORE, recvdblock + +.if LOAD_TO_RAM_UNDER_IO +recvdblock: jmp gotblock + +getblockio: plp + sta storebytio + 2 + lda storebyte + 1 + sta storebytio + 1 + + RECEIVE STOREBYTE_ALLRAM, gotblock +.endif; LOAD_TO_RAM_UNDER_IO + +EARLY_RECEIVE_RTS = HAVE_ENDGETBLOCK | END_ADDRESS_API | OPENFILE_POLLBLOCK + +.if EARLY_RECEIVE_RTS +.ifdef recvdblock +gotblock: +.else +recvdblock: +.endif + +.endif; EARLY_RECEIVE_RTS + +.if END_ADDRESS_API + + ldx storebyte + 1 + cpx endaddrlo + ldy storebyte + 2 + iny + tya + sbc endaddrhi + bcc :+ + stx endaddrlo + sty endaddrhi +: ldy #0 + +.endif; END_ADDRESS_API + +.if HAVE_ENDGETBLOCK + ENDGETBLOCK +.endif; HAVE_ENDGETBLOCK + +.if EARLY_RECEIVE_RTS + .if OPENFILE_POLLBLOCK + lda #diskio::status::OK + .endif; OPENFILE_POLLBLOCK + clc + rts + +.endif; EARLY_RECEIVE_RTS + + +.if OPENFILE_POLLBLOCK +.export openfile +.endif; OPENFILE_POLLBLOCK + +openfile: +.if LOADCOMPD_TO + lda #$00 + sta loadaddroffslo + sta loadaddroffshi +.endif; LOADCOMPD_TO +.if LOAD_TO_API + lda #OPC_LDA_ZP + bcs :+ +loadcompdopen: + lda #OPC_STA_ZP +: sta storeladrl + sta storeladrh +.else; !LOAD_TO_API +loadcompdopen: +.endif; !LOAD_TO_API + +.if MEM_DECOMP_TO_API + lda #OPC_STA_ZP + sta storedadrl + sta storedadrh +.endif; MEM_DECOMP_TO_API + +.if LOAD_VIA_KERNAL_FALLBACK + .if LOAD_COMPD_API + lda #0 + sta STREAMAVAIL + .endif; LOAD_COMPD_API + + BRANCH_IF_INSTALLED :+ + jmp kernalopen +: +.endif; LOAD_VIA_KERNAL_FALLBACK + + jsr wakeup + +: BRANCH_IF_BLOCK_NOT_READY :- + + ;ldy #0 +sendname: lda (BLOCKDESTLO),y + pha + SENDBYTE sendstore + iny + pla + bne sendname + +.if LOAD_VIA_KERNAL_FALLBACK + ; check whether the loader is still installed + ldx #KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY - 1; some delay until the drive side is ready +: dex + bpl :- + SET_FLAGS_N_DATA_V_CLK + bvc :+ + + ; if not, try again with KERNAL routines + ldx BLOCKDESTLO + ldy BLOCKDESTLO + 1 + jmp kernalopen + +: +.endif; LOAD_VIA_KERNAL_FALLBACK + +.if DECOMPRESSOR = DECOMPRESSORS::ZX0 + .if USE_DALI + .else + ldy #$40; .lz_bits + .endif +.endif; DECOMPRESSOR = DECOMPRESSORS::ZX0 + +wakeup: stx BLOCKINDEX; = BLOCKDESTLO, filename pointer lobyte parameter + sty BLOCKINDEX + 1 + + ldy #0 +.if HAVE_GETBYTE + stx getstrmbyt + 1 +.endif; HAVE_GETBYTE +.if LOAD_COMPD_API + sty STREAMAVAIL +.endif; LOAD_COMPD_API +.if END_ADDRESS_API + sty endaddrlo + sty endaddrhi +.endif; END_ADDRESS_API + CLEAR sendstore + RECEIVE_SETUP + +.if LOAD_VIA_KERNAL_FALLBACK + clc +.endif; LOAD_VIA_KERNAL_FALLBACK + +.ifndef recvdblock +recvdblock: +.endif +.ifndef gotblock +gotblock: +.endif + rts + +.macro POLLBLOCK + .local noblock + + .if LOAD_COMPD_API + .if LOAD_TO_RAM_UNDER_IO + BUFFER_MEMCONFIG + ENABLE_IO_SPACE + .endif; LOAD_TO_RAM_UNDER_IO + + .if LOAD_VIA_KERNAL_FALLBACK + BRANCH_IF_NOT_INSTALLED noblock + .endif; LOAD_VIA_KERNAL_FALLBACK + + BRANCH_IF_BLOCK_NOT_READY noblock + + .if POLLBLOCK_PRESERVE_X + txa + pha + .endif; POLLBLOCK_PRESERVE_X + jsr pollblock + .if POLLBLOCK_PRESERVE_X + pla + tax + .endif; POLLBLOCK_PRESERVE_X +noblock: + .if LOAD_TO_RAM_UNDER_IO + RESTORE_MEMCONFIG_Y + ldy #0 + .endif; LOAD_TO_RAM_UNDER_IO + .endif; LOAD_COMPD_API +.endmacro + +.macro GETBLOCK page + .local getblock + .local eof + + .if LOAD_TO_RAM_UNDER_IO + BUFFER_MEMCONFIG + ENABLE_IO_SPACE + .endif; LOAD_TO_RAM_UNDER_IO + +getblock: jsr pollblock + bcs eof + lda STREAMAVAIL; add 1 for read 254/use 256 bytes STREAMAVAIL + asl ; inaccuracy starting with 128 file blocks + lda #READAHEAD + 1; +1 because the following sbc will subtract one too many due to cleared carry + adc page + ;clc + sbc loadaddrhi + cmp STREAMAVAIL + bcs getblock +eof: + .if LOAD_TO_RAM_UNDER_IO + RESTORE_MEMCONFIG_Y + ldy #0 + .endif; LOAD_TO_RAM_UNDER_IO +.endmacro + +.if HAVE_GETBYTE + +getbyte: inc getstrmbyt + 1 + beq :+ + + .if LOAD_TO_RAM_UNDER_IO + ENABLE_IO_SPACE + .endif; LOAD_TO_RAM_UNDER_IO + + .if LOAD_VIA_KERNAL_FALLBACK + BRANCH_IF_NOT_INSTALLED retstrmbyt + .endif; LOAD_VIA_KERNAL_FALLBACK + + BRANCH_IF_BLOCK_READY :++ + +retstrmbyt: + .if LOAD_TO_RAM_UNDER_IO + ENABLE_ALL_RAM + .endif; LOAD_TO_RAM_UNDER_IO + +getstrmbyt: lda $ff00 + rts + +: inc getstrmbyt + 2 + +: php + txa + pha + tya + pha + GETBLOCK getstrmbyt + 2 + pla + tay + pla + tax + plp + jmp retstrmbyt + +.endif; HAVE_GETBYTE + +.if FILE_EXISTS_API + +.export fileexists + + ; --- check if file exists --- + ; in: x/y - x: lo, y: hi to 0-terminated filename string + ; out: c - set on file not found or error + ; a - status +fileexists: + .if LOAD_VIA_KERNAL_FALLBACK + BRANCH_IF_INSTALLED xinstalled + + BUFFER_MEMCONFIG + ENABLE_KERNAL_SERIAL_ROUTINES + jsr :+ + RESTORE_MEMCONFIG_Y + rts + +: jsr openfile + bcs xreturn + ldx LA + jsr CHKIN + jsr BASIN + lda STATUS + pha + jsr KERNLCLOSE + pla + beq :+ + lda #diskio::status::FILE_NOT_FOUND + sec + rts +: OK_CLC + rts +xinstalled: + .endif; LOAD_VIA_KERNAL_FALLBACK + + jsr openfile +xblknotrdy: POLL_BLOCK xeofidle, xblknotrdy, xpollfail + CLOSE_FILE + lda #diskio::status::FILE_NOT_FOUND + bcc xreturn + lda #diskio::status::OK + SKIPWORD +xeofidle: +xpollfail: lda #diskio::status::DEVICE_NOT_PRESENT +xreturn: cmp #diskio::status::OK + 1 + rts + +.endif; FILE_EXISTS_API + +.if CLOSE_FILE_API + +.export closefile + + ; ----- close an open file ----- + ; in: nothing + ; out: undefined +closefile: + .if LOAD_VIA_KERNAL_FALLBACK + BRANCH_IF_INSTALLED cinstalled + + BUFFER_MEMCONFIG + ENABLE_KERNAL_SERIAL_ROUTINES + jsr KERNLCLOSE + RESTORE_MEMCONFIG_Y + rts +cinstalled: + .endif; LOAD_VIA_KERNAL_FALLBACK + +cblknotrdy: POLL_BLOCK ceofidle, cblknotrdy, cpollfail + CLOSE_FILE +ceofidle: +cpollfail: rts + +.endif; CLOSE_FILE_API + +.if LOAD_VIA_KERNAL_FALLBACK + +.export KERNLCLOSE + +kernalopen: BUFFER_MEMCONFIG + ENABLE_KERNAL_SERIAL_ROUTINES + jsr :+ + RESTORE_MEMCONFIG_Y + rts +: + .if KERNAL_FALLBACK_SEI_WORKAROUNDS + ENABLE_WAITBUSY_KERNAL + .endif; KERNAL_FALLBACK_SEI_WORKAROUNDS + + stx namestrpos + sty namestrpos + 1 + ldx #$ff +: inx +namestrpos = * + 1 + lda a:$00,x + bne :- + txa + pha; name length + + .if PLATFORM = diskio::platform::COMMODORE_128 + lda #0 + sta FNLEN + lda #COMMAND_ERROR_CHANNEL + ldx FA + tay + jsr SETLFS + jsr OPEN + bcs burstopner + + .if KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS + + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + + CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN +: SET_FLAGS_N_DATA_V_CLK + bpl :- + + ldx #$b8; delay +: dex ; of + bne :- ; ~1 ms + + lda FA + sta DFLTO + ora #SA_LISTEN + sta BSOUR + sei + jsr NOEOI; send byte + lda #SA_OPENCHANNEL | COMMAND_ERROR_CHANNEL + jsr SECND; send listen secondary address + + POP_CLOCKCONFIG + + .else; !KERNAL_FALLBACK_SEI_WORKAROUNDS + ldx LA + jsr CKOUT + .endif; !KERNAL_FALLBACK_SEI_WORKAROUNDS + bcc :+ +burstopner: tax + pla; name length + txa + jmp kernlopner + +: ldy #burstldcme - burstldcmd +: lda burstldcmd - 1,y + jsr BSOUT + dey + bne :- + pla; name length + sta FNLEN + lda namestrpos + sta FNADR + lda namestrpos + 1 + sta FNADR + 1 +: lda (FNADR),y + jsr BSOUT + iny + cpy FNLEN + bne :- + jsr CLRCH + bit SERIAL + bvs burstload + + lda FNLEN + pha + lda LA + jsr CLOSE + jmp noburstlod + +burstload: START_BURST_LOAD + + jmp kernalfinp + +getburstby: GET_BURST_BYTE + rts + +burstldcmd: .byte $1f, "0u"; read backwards +burstldcme: + +noburstlod: + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + PREPARE_PARALLEL_CHECK + + lda #KERNALFILENO + ldx FA + ldy #$00 + jsr SETLFS + pla; name length + ldx namestrpos + ldy namestrpos + 1 + jsr SETNAM + jsr OPEN + bcs kernlopner + + .if KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS + BRANCH_IF_DRIVE_PARALLEL noseiwrkar + + .if USE_2_MHZ + PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK + .endif + CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN +: SET_FLAGS_N_DATA_V_CLK + bpl :- + ldx #$b8; delay +: dex ; of + bne :- ; ~1 ms + lda FA + sta DFLTN + ora #SA_TALK + sta BSOUR + sei + jsr NOEOI; send byte + lda #SA_OPENCHANNEL; secondary address + jsr TKSA; send talk secondary address + .if USE_2_MHZ + POP_CLOCKCONFIG + .endif + jmp kernalfinp +noseiwrkar: + .endif; !KERNAL_FALLBACK_SEI_WORKAROUNDS + + ldx LA + jsr CHKIN + +kernalfinp: ldx #$ff + stx KERNALBLKC + .if HAVE_GETBYTE + stx getstrmbyt + 1 + .endif + + ; file not found is not detected at this point + ; but after "getting" the load address, + ; the busy LED will keep flashing + + OK_CLC + rts + +kernlopner: tax + lda #diskio::status::DEVICE_NOT_PRESENT + cpx #OPEN_DEVICENOTPRESENT + beq :+ + lda #diskio::status::GENERIC_KERNAL_ERROR +: sec; error + rts + +kernalgblk: + .if LOAD_COMPD_API + clc + lda #READAHEAD + 1; +1 because the following sbc will subtract one too many due to cleared carry + .if ZP_READ_PTR + adc .lobyte(decompsrc + 1) + .else + adc decompsrc + 1 + .endif + ;clc + sbc loadaddrhi + cmp STREAMAVAIL + bcs :+ + rts; requested page is already in memory +: + .endif; LOAD_COMPD_API + + BUFFER_MEMCONFIG + ENABLE_KERNAL_SERIAL_ROUTINES + jsr dokernalgb + RESTORE_MEMCONFIG_Y + .if LOAD_COMPD_API + bcc kernalgblk + beq :+ + .else; !LOAD_COMPD_API + beq :+ + bcc :+ + .endif; !LOAD_COMPD_API + jmp pollfail +: rts + +dokernalgb: ldx KERNALBLKC + bne kernalgble + + ldx #$fe; $0100 bytes minus 2 bytes for track/sector link + + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc :+ + jsr gburstatus + bcc :+ + jmp kernlgberr +: + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + stx KERNALBLKC + +kernalgble: cpx #$ff + bne kernalgblp + + inc KERNALBLKC; EOF may happen in first block + + ; retrieve load address + + ldx #$fc; $0100 bytes minus 2 bytes track/sector link and 2 bytes load address + + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc burstopnok + jsr getburstby; status + cmp #KERNAL_STATUS_ERROR_BURST + bcc burstopnok + bne :+ + lda #diskio::status::FILE_NOT_FOUND +: cmp #KERNAL_STATUS_EOF_BURST + beq :+ + ; this is a job error code + jmp KERNLCLOSE +: jsr getburstby + tax + dex; load address + dex +burstopnok: + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + .if LOAD_TO_API + lda #OPC_STA_ZP + cmp storeladrl + beq nokrnlodto + + lda loadaddrlo + sta endaddrlo + lda loadaddrhi + sta endaddrhi + + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc :+ + jsr getburstby; skip load + jsr getburstby; address + jmp kern1stblk +: + .endif; PLATFORM = diskio::platform::COMMODORE_128 + jsr BASIN; skip load + jsr BASIN; address + jmp kern1stblk +nokrnlodto: + .endif; LOAD_TO_API + + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc :+ + jsr getburstby + jmp :++ +: jsr BASIN +: + .else; !PLATFORM = diskio::platform::COMMODORE_128 + jsr BASIN + .endif; !PLATFORM = diskio::platform::COMMODORE_128 + .if LOADCOMPD_TO + clc + adc loadaddroffslo + php + .endif; LOADCOMPD_TO + sta loadaddrlo +kernalstrl: sta endaddrlo + SET_DECSRC_LO + + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc :+ + jsr getburstby + jmp :++ +: jsr BASIN +: + .else; !PLATFORM = diskio::platform::COMMODORE_128 + jsr BASIN + .endif; !PLATFORM = diskio::platform::COMMODORE_128 + .if LOADCOMPD_TO + plp + adc loadaddroffshi + .endif; LOADCOMPD_TO + sta loadaddrhi +kernalstrh: sta endaddrhi + SET_DECSRC_HI + +kern1stblk: lda STATUS + bne kernfnotf; #diskio::status::FILE_NOT_FOUND; cannot determine type of error + +kernalgblp: + .if PLATFORM = diskio::platform::COMMODORE_128 + bit SERIAL + bvc :+ + jsr getburstby + jmp kernbytldd +: + .endif; PLATFORM = diskio::platform::COMMODORE_128 + + lda STATUS + bne kernlgberr + + .if KERNAL_FALLBACK_SEI_WORKAROUNDS + BRANCH_IF_DRIVE_PARALLEL :++ +: BRANCH_IF_BLOCK_NOT_READY :- +: + .endif; KERNAL_FALLBACK_SEI_WORKAROUNDS + + jsr BASIN + + ldy STATUS + beq kernbytldd + cpy #KERNAL_STATUS_EOF; $40 + bne KERNLCLOSE + pha + tya + jsr KERNLCLOSE + pla + +kernbytldd: + .if LOAD_TO_RAM_UNDER_IO + ENABLE_ALL_RAM_Y + .endif; LOAD_TO_RAM_UNDER_IO + + ldy #0 + sta (endaddrlo),y + + .if LOAD_TO_RAM_UNDER_IO + ENABLE_KERNAL_SERIAL_ROUTINES + .endif; LOAD_TO_RAM_UNDER_IO + + inc endaddrlo + bne :+ + inc endaddrhi +: dex + bne kernalgblp + + stx KERNALBLKC + + .if LOAD_COMPD_API + clc + lda endaddrhi + sbc loadaddrhi + bcs :+ + lda #0 +: sta STREAMAVAIL + .endif; LOAD_COMPD_API + + clc +kernalbeof: lda #diskio::status::OK + rts + +kernlgberr: cmp #KERNAL_STATUS_EOF; $40 + beq kernalbeof; carry is set when branching + sec + rts + + .if PLATFORM = diskio::platform::COMMODORE_128 +gburstatus: lda STATUS + cmp #KERNAL_STATUS_ERROR_BURST + bcc :++ + cmp #KERNAL_STATUS_EOF_BURST + bne :+ + lda #KERNAL_STATUS_EOF; $40 + sta STATUS +: jsr KERNLCLOSE + bcs :++ + +: jsr getburstby + cmp #KERNAL_STATUS_ERROR_BURST + bcc :+ + sta STATUS + cmp #KERNAL_STATUS_EOF_BURST + bne :--; branch if not EOF, this is a job error code + jsr getburstby + tax + clc +: rts + .endif; PLATFORM = diskio::platform::COMMODORE_128 + +kernfnotf: lda #diskio::status::FILE_NOT_FOUND; cannot determine type of error + + ; EOF or error, close file +KERNLCLOSE: pha; KERNAL status byte + + lda #KERNALFILENO + jsr CLOSE + jsr CLALL + jsr CLRCH + + pla; KERNAL status byte + .if PLATFORM = diskio::platform::COMMODORE_16 + sta STATUS; the CLRCH call above sets STATUS ($90) + .endif; PLATFORM = diskio::platform::COMMODORE_16 + cmp #KERNAL_STATUS_EOF; $40 + bne kernaloerr + ;sec + rts; EOF +kernaloerr: sec + tax + cmp #diskio::status::DEVICE_NOT_PRESENT + bcs :+ + lda #diskio::status::GENERIC_KERNAL_ERROR + sec +: rts + +.endif; LOAD_VIA_KERNAL_FALLBACK + +.if MEM_DECOMP_API + +.export memdecomp + + ; --- decompress a compressed file from memory --- + ; in: x/y - lo/hi of compressed file in memory + ; c - if MEMDECOMP_TO_API != 0, c = 0: decompress to address as stored in the file + ; c = 1: decompress to caller-specified address (decdestlo/hi) + ; out: undefined + +memdecomp: + .if MEM_DECOMP_TO_API + lda #OPC_STA_ZP + bcc :+ + lda #OPC_LDA_ZP +: sta storedadrl + sta storedadrh + .endif; MEM_DECOMP_TO_API + + stx loadaddrlo + sty loadaddrhi + + .if DECOMPRESSOR = DECOMPRESSORS::ZX0 + txa + bne :+ + dey +: dex + .endif + + .if ZP_READ_PTR + stx .lobyte(decompsrc) + .endif; ZP_READ_PTR + .if HAVE_GETBYTE + dex + stx decompsrc + .endif; HAVE_GETBYTE + .if ZP_READ_PTR + sty .lobyte(decompsrc + 1) + .else; !ZP_READ_PTR + .if HAVE_GETBYTE + cpx #$ff + bne :+ + dey +: + .endif; HAVE_GETBYTE + sty decompsrc + 1 + .endif; !ZP_READ_PTR + ldy #0 + + .if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH + jsr decompress + ; return the execution address in x/y + ldx lo + 1 + ldy hi + 1 + rts + .else + .if DECOMPRESSOR = DECOMPRESSORS::ZX0 + .if USE_DALI + .else + ldx #$40 + stx BLOCKINDEX + 1; .lz_bits + .endif + .endif + jmp decompress + .endif + +.endif; MEM_DECOMP_API + +.if UNINSTALL_API + +.export uninstall + + ; --- uninstall the loader --- + ; in: nothing + ; out: undefined +uninstall: DO_UNINSTALL + rts + +.endif; UNINSTALL_API + + +.if DECOMPRESSOR = DECOMPRESSORS::BITNAX + .export DECOMPRESSOR_BITNAX = DECOMPRESSOR + .include "decompress/bitnaxdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::BYTEBOOZER2 + .export DECOMPRESSOR_BYTEBOOZER2 = DECOMPRESSOR + .include "decompress/b2decomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::DOYNAX_LZ + .export DECOMPRESSOR_DOYNAX_LZ = DECOMPRESSOR + .include "decompress/doynaxdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::EXOMIZER + .export DECOMPRESSOR_EXOMIZER = DECOMPRESSOR + .include "decompress/exodecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::LEVELCRUSH + .export DECOMPRESSOR_LEVELCRUSH = DECOMPRESSOR + .include "decompress/lcdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::LZSA2 + .export DECOMPRESSOR_LZSA2 = DECOMPRESSOR + .include "decompress/lzsa2decomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::NUCRUNCH + .export DECOMPRESSOR_NUCRUNCH = DECOMPRESSOR + .include "decompress/ncdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH + .export DECOMPRESSOR_PUCRUNCH = DECOMPRESSOR + .include "decompress/pudecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::SUBSIZER + .export DECOMPRESSOR_SUBSIZER = DECOMPRESSOR + .include "decompress/subsizerdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::TINYCRUNCH + .export DECOMPRESSOR_TINYCRUNCH = DECOMPRESSOR + .include "decompress/tcdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH + .export DECOMPRESSOR_TSCRUNCH = DECOMPRESSOR + .include "decompress/tsdecomp.s" + +.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0 + .export DECOMPRESSOR_ZX0 = DECOMPRESSOR + .include "decompress/zx0decomp.s" + +.else + .export DECOMPRESSOR_NONE = DECOMPRESSORS::NONE +.endif + + CHECK_RESIDENT_END_ADDRESS diff --git a/loader/src/save.s b/loader/src/save.s new file mode 100644 index 0000000..b54bed8 --- /dev/null +++ b/loader/src/save.s @@ -0,0 +1,1145 @@ + +__NO_SAVE_SYMBOLS_IMPORT = 1 +.include "loader.inc" + +.include "drives/drivecode-common.inc" +.include "hal/hal.inc" + +.include "cpu.inc" +.include "kernal.inc" + +.segment "DISKIO_PLUGIN_ZP" : zeropage + +PARAMSPTR: .res 2 +SAVEPTR: .res 2 +LENGTH: .res 2 +BLOCKCNT: .res 1 +BYTECOUNT: .res 1 + +MAXBLOCKS = 64 + +.segment "DISKIO_PLUGIN" + +.ifdef TRNSADDR + .org TRNSADDR - 2 + .word * + 2; load address +.endif + +.exportzp status_FILE_TOO_LARGE = diskio::status::FILE_TOO_LARGE +.exportzp status_FILE_ON_DISK_TOO_SMALL = diskio::status::FILE_ON_DISK_TOO_SMALL +.exportzp status_FILE_ON_DISK_TOO_LARGE = diskio::status::FILE_ON_DISK_TOO_LARGE +.exportzp status_WRITE_PROTECT_ON = diskio::status::WRITE_PROTECT_ON + +WRITE_PROTECTED = $fe + +SAVE_FROM_RAM_UNDER_IO = LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16) + +.if LOAD_VIA_KERNAL_FALLBACK +.import KERNLCLOSE +.endif; LOAD_VIA_KERNAL_FALLBACK + +.import sendbyte +.import receivbyte + +.export save + +save: stx PARAMSPTR + sty PARAMSPTR + 1 + + ldy #saveparams::buffer + lda (PARAMSPTR),y + sta swapparams + iny + lda (PARAMSPTR),y + sta swapparams + 1 + + ldy #saveparams::filename + lda (PARAMSPTR),y + sta SAVEPTR + iny + lda (PARAMSPTR),y + sta SAVEPTR + 1 + dey + ;ldy #0 +: lda (SAVEPTR),y + beq :+ + sta savename41 - drvsaver41 + saver41,y +.if ONLY_1541_AND_COMPATIBLE = 0 + sta savename71 - drvsaver71 + saver71,y + sta savename81 - drvsaver81 + saver81,y +.endif; ONLY_1541_AND_COMPATIBLE + + iny + cpy #FILENAME_MAXLENGTH + bcc :- +: cpy #0 + bne :+ + lda #diskio::status::FILE_NOT_FOUND + sec + rts + +: ldy #saveparams::length + clc + lda #2 + adc (PARAMSPTR),y + sta LENGTH + tax + iny + lda #0 + adc (PARAMSPTR),y + sta LENGTH + 1 + tay + + lda #0 + sta BLOCKCNT + clc +: inc BLOCKCNT + bne :+ + dec BLOCKCNT +: txa + sbc #$fe + tax + tya + sbc #0 + tay + bcs :-- + + lda BLOCKCNT + cmp #MAXBLOCKS + 1 + bcc :+ + lda #diskio::status::FILE_TOO_LARGE + rts + +: ldx #.lobyte(swapparams) + ldy #.hibyte(swapparams) + jsr swapdrvcod + bcc saverinstd + +.if LOAD_VIA_KERNAL_FALLBACK + + cmp #diskio::status::DEVICE_INCOMPATIBLE + beq :+ + sec + rts +: + ldx SAVEPTR + ldy SAVEPTR + 1 + BUFFER_MEMCONFIG + ENABLE_KERNAL_SERIAL_ROUTINES + jsr kernalsave + RESTORE_MEMCONFIG_Y +.endif; !LOAD_VIA_KERNAL_FALLBACK + rts + +saverinstd: SET_FLAGS_N_DATA_V_CLK + bmi saverinstd + SYNC +: SET_FLAGS_N_DATA_V_CLK + bpl :- + CLEAR +: SET_FLAGS_N_DATA_V_CLK + bvs :- + +waitready: SET_FLAGS_N_DATA_V_CLK + bvc waitready + + ldy #saveparams::from + lda (PARAMSPTR),y + sta SAVEPTR + iny + lda (PARAMSPTR),y + sta SAVEPTR + 1 + + ; get status/number of file blocks + jsr receivbyte + beq devnotpres + cmp #WRITE_PROTECTED + beq writeprot + cmp #diskio::status::FILE_NOT_FOUND; = $ff + beq savereturn + + cmp BLOCKCNT + beq :+ + + php + lda #0 + jsr sendbyte + plp + lda #diskio::status::FILE_ON_DISK_TOO_SMALL + bcc savereturn + lda #diskio::status::FILE_ON_DISK_TOO_LARGE + bcs savereturn; jmp + +: jsr sendcount + + ldy #saveparams::loadaddress + lda (PARAMSPTR),y + jsr sendbyte + dec BYTECOUNT + iny + lda (PARAMSPTR),y + jsr sendbyte + dec BYTECOUNT + +.if SAVE_FROM_RAM_UNDER_IO + BUFFER_MEMCONFIG +.endif + jsr sendloop + beq savedone + +: jsr sendblock + bne :- +savedone: +.if SAVE_FROM_RAM_UNDER_IO + RESTORE_MEMCONFIG_Y +.endif + lda #diskio::status::OK + SKIPWORD +writeprot: lda #diskio::status::WRITE_PROTECT_ON + SKIPWORD +devnotpres: lda #diskio::status::DEVICE_NOT_PRESENT +savereturn: + pha + jsr restoreldr + pla + cmp #diskio::status::OK + 1 + rts + +sendblock: SET_FLAGS_N_DATA_V_CLK + bvc sendblock + + jsr sendcount + +sendloop: ldy #0 +: +.if SAVE_FROM_RAM_UNDER_IO + ENABLE_ALL_RAM_X + lda (SAVEPTR),y + ENABLE_IO_SPACE_X +.else + lda (SAVEPTR),y +.endif + jsr sendbyte + iny + dec BYTECOUNT + bne :- + + clc + tya + adc SAVEPTR + sta SAVEPTR + bcc :+ + inc SAVEPTR + 1 +: + CLEAR + + dec BLOCKCNT + rts + +sendcount: lda #$fe + ldx LENGTH + 1 + bne :+ + ldx LENGTH + cpx #$fe + bcs :+ + txa +: sta BYTECOUNT + + sec + lda LENGTH + sbc BYTECOUNT + sta LENGTH + bcs :+ + dec LENGTH + 1 + +: lda BYTECOUNT + jmp sendbyte + +saver41: + .org $05ba; loadfile41 + + .assert * <= loadfile41, error, "***** 1541 save code above loadfile41 position. *****" + .assert * >= loadfile41, error, "***** 1541 save code below loadfile41 position. *****" + +drvsaver41: .scope cbm1541 + +.include "via.inc" + + .assert (filesectors + MAXBLOCKS + 1) <= wdogentr41, error, "***** 1541 track/sector buffer too small. *****" + +filetracks = idxloop41 +filesectors = idxloop41 + MAXBLOCKS + 1 +writeblock = $0510 +rawblock = $0700 + +CURRBLOCK = $06 +SENDVALUE = $09 +TRKSEC = SENDVALUE +BLOCKSIZE = $0e +NUMBLOCKS = $74 + +DSKID = $12 +BUFPNT = $30 +HDRPNT = $32 +HBID = $39 +DRIVE = $3d +DBID = $47 + +SRCH = $f510 +SRCH20 = $f538 +SRCH30 = $f54e +ERR = $f553 +WRT05 = $f575 +WRT10 = $f586 +CHKBLK = $f5e9 +WRITEBLOCKLEN = CHKBLK - $10 - SRCH; $c9 + +TIMER = VIA2_T1C_H + + bmi filenotfnd + + lda #BUSY_LED | MOTOR + jsr setbv2b41 + ;ldy FILETRACK + lda FILESECTOR41 + bcs filefound; jmp + +RESTORELOOP41 = $d9 +RESTOREMAIN41 = RESTORELOOP41 + 1 + + ; FILENAMEHASHVALSLO and NUMBLOCKS, will possibly be overwritten +saveentry: lda #ATNA_OUT | DATA_OUT + ldx #RESTOREMAIN41 + sta (.lobyte($03 - RESTOREMAIN41),x); VIA1_PRB + +: lda $00,x + sta .lobyte($0100 - RESTOREMAIN41),x + cpx #$0100 - 21 + bcc :+ + lda SECTORLINKTABLE41 + 21,x + sta RESTORELOOP41 + 21,x +: inx + bne :-- + + jsr initlink41 + + ;ldy #0 +: lda #CLK_IN + and (V1B41),y; VIA1_PRB + beq :- + lda #ATNA_OUT | CLK_OUT + sta (V1B41),y; VIA1_PRB +: lda (V1B41),y; VIA1_PRB + lsr + bcs :- + ldx #$ff +: inx +saveinit: lda savename41,x + sta FILENAME41,x + bne :- + sta NUMBLOCKS + + lda (V2B41),y; VIA2_PRB + ldy #WRITE_PROTECTED + and #WRITE_PROTECT + beq writeprot + + jsr findfile41; SP = $08 -> $06 + +enumblocks: ldx NUMBLOCKS + lda filesectors,x + ldy filetracks,x + jsr getblock41 + bcc enumblocks + + inc NUMBLOCKS + lda LINKSECTOR41 + ldy LINKTRACK41 +filefound: ldx NUMBLOCKS + cpx #MAXBLOCKS + 1 + bcs :+ + sta filesectors,x + tya + sta filetracks,x + bne enumblocks +: + ldy NUMBLOCKS + +filenotfnd: ; y = diskio::status::FILE_NOT_FOUND = $ff +writeprot: ; y = WRITE_PROTECTED = $fe + + .assert getbyterts41 < writeblock, error, "***** getbyte runs into writeblock *****" + + ldx #WRITEBLOCKLEN + 1 + lda #OPC_RTS +: sta writeblock - 1,x + lda SRCH - 2,x + dex + bne :- + inc writeblock + WRT10 - 3 - SRCH; OPC_JMP_ABS -> OPC_EOR_ABS, no write protect check + lda #OPC_TXA + sta writeblock + SRCH30 - SRCH + inc writeblock + ERR - SRCH; OPC_JMP_ABS -> OPC_EOR_ABS, no sync timeout check + stx BUFPNT + stx HDRPNT + 1 + stx DRIVE + inx +: lda ID041,x + jsr sertoraw41 + sta DSKID,x + dex + bpl :- + stx TIMER + ldx #.hibyte(writeblock) + stx writeblock + SRCH20 + 2 - SRCH + stx writeblock + WRT10 + 5 - SRCH + + pla + + tya + ENABLE_WATCHDOG + DRIVESENDBYTE 1541, SENDVALUE + + ;ldy #0 + beq saveloopin; jmp + +saveloop: jsr rdygetbyte + beq saverexit; branch if file on disk too small/large + + sta BLOCKSIZE + + ;ldx #0 +: jsr getbytewdog41 + sta rawblock + 2,x + inx + cpx BLOCKSIZE + bcc :- + + lda #ATNA_OUT | CLK_OUT | DATA_OUT + sta VIA1_PRB + sei + + lda #0 +: sta rawblock + 2,x + inx + bne :- + + ldx CURRBLOCK + ldy filesectors + 1,x + lda filetracks + 1,x + sta rawblock + bne :+ + ldy BLOCKSIZE + iny +: sty rawblock + 1 + ldy filetracks,x + sty TRKSEC + lda filesectors,x + sta TRKSEC + 1 + jsr trkseek41 + ldx #.lobyte(TRKSEC) + stx HDRPNT + dex + stx HBID; 8 + dex + stx DBID; 7 + ;ldx #.hibyte(rawblock) + stx BUFPNT + 1 + jsr writeblock + WRT05 - SRCH + ldy CURRBLOCK + iny +saveloopin: sty CURRBLOCK + cpy NUMBLOCKS + bcc saveloop + +saverexit: lda #OPC_JSR_ABS + sta restore + lda CURRTRACK41 + pha + +rdygetbyte: ldx #0 + lda #ATNA_OUT + sta (V1B41,x); VIA1_PRB +: +restore: jmp getbytewdog41 + sta $00,x + inx + bne :- + sei + + pla + sta CURRTRACK41 + + jmp RESTOREMAIN41 + + .endscope + +savename41: .res FILENAME_MAXLENGTH + .byte 0 + + .assert * <= $0700, error, "***** 1541 save code too large. *****" + + .reloc +saver41end: + +.if ONLY_1541_AND_COMPATIBLE = 0 + +saver71: + .org $04e1; loadfile71 + + .assert * <= loadfile71, error, "***** 1571 save code above loadfile71 position. *****" + .assert * >= loadfile71, error, "***** 1571 save code below loadfile71 position. *****" + +drvsaver71: .scope cbm1571 + +.include "via.inc" + + .assert (filesectors + MAXBLOCKS + 1) <= rawblock, error, "***** 1571 track/sector buffer too small. *****" + +writeblock = $0310 +rawblock = $0700 + +NUMBLOCKS = $1b +CURRBLOCK = $1c +BLOCKSIZE = $1d +SENDVALUE = $1e +TRKSEC = SENDVALUE + +BUFPNT = $30 +HDRPNT = $32 +HBID = $39 +DRIVE = $3d +DBID = $47 + +SRCH = $f510 +SRCH20 = $f538 +SRCH30 = $f54e +ERR = $f553 +WRT05 = $f575 +WRT10 = $f586 +CHKBLK = $f5e9 +WRITEBLOCKLEN = CHKBLK - $10 - SRCH + +TIMER = VIA2_T1C_H + + bcc aftfindfil + ;lda #diskio::status::FILE_NOT_FOUND; $ff + jmp filenotfnd + +.import CUSTOMZPBUFFSIZE71 + + lda #ATNA_OUT | DATA_OUT + sta VIA1_PRB + + ldx #0 +: lda CUSTOMZPBUFFER71,x + sta $00,x + inx + cpx #.lobyte(CUSTOMZPBUFFSIZE71) + bne :- + + ldx #0 + stx NUMBLOCKS + +: lda #CLK_IN + and VIA1_PRB + beq :- + lda #ATNA_OUT | CLK_OUT + sta VIA1_PRB +: lda VIA1_PRB + lsr + bcs :- + + lda #WRITE_PROTECT + and VIA2_PRB + beq writeprot + + ;ldx #0 +dosave: lda savename71,x + beq :+ + sta FILENAME71,x + inx + bne dosave +: + sec + jmp findfile71 + +aftfindfil: lda #BUSY_LED | MOTOR + jsr bsetv2b71 + lda DIRSECTORS71,x + bcc :+; jmp + +enumblocks: ldx NUMBLOCKS + lda filesectors,x + ldy filetracks,x + jsr getblock71 + bcs enumblocks + + lda LINKSECTOR71 + ldy LINKTRACK71 + inc NUMBLOCKS +: ldx NUMBLOCKS + cpx #MAXBLOCKS + 1 + bcs :+ + sta filesectors,x + tya + sta filetracks,x + bne enumblocks +: + ldx #WRITEBLOCKLEN +: lda SRCH - 1,x + sta writeblock - 1,x + dex + bne :- + inc writeblock + WRT10 - 3 - SRCH; OPC_JMP_ABS -> OPC_EOR_ABS, no write protect check + lda #.hibyte(writeblock) + sta writeblock + SRCH20 + 2 - SRCH + sta writeblock + WRT10 + 5 - SRCH + lda #OPC_TXA + sta writeblock + SRCH30 - SRCH + inc writeblock + ERR - SRCH; OPC_JMP_ABS -> OPC_EOR_ABS, no sync timeout check + lda #OPC_RTS + sta writeblock + WRITEBLOCKLEN + stx BUFPNT + stx HDRPNT + 1 + stx DRIVE + lda #.lobyte(TRKSEC) + sta HDRPNT + ldx #$08 + stx HBID + dex + stx DBID + ;ldy #.hibyte(rawblock) + stx BUFPNT + 1 + + lda NUMBLOCKS + SKIPWORD +writeprot: lda #WRITE_PROTECTED +filenotfnd: + ldx #.lobyte(trkseek71) - 1 + txs + + ldy #$ff + sty TIMER + ENABLE_WATCHDOG + DRIVESENDBYTE 1571, SENDVALUE + + lda NUMBLOCKS + beq saverexit + + ;ldy #0 +saveloop: sty CURRBLOCK + + jsr rdygetbyte + beq saverexit; branch if file on disk too small/large + sta BLOCKSIZE + + ;ldx #0 +: jsr getbyte + sta rawblock + 2,x + inx + cpx BLOCKSIZE + bcc :- + + lda #ATNA_OUT | CLK_OUT | DATA_OUT + sta VIA1_PRB + sei + + lda #0 + beq :++; jmp +: sta rawblock + 2,x + inx +: cpx #$fe + bcc :-- + + ldx CURRBLOCK + ldy filesectors + 1,x + lda filetracks + 1,x + sta rawblock + bne :+ + ldy BLOCKSIZE + iny +: sty rawblock + 1 + ldy filetracks,x + sty TRKSEC + lda filesectors,x + sta TRKSEC + 1 + sta CLEARSECTORLINKTABLE71 + jsr trkseek71 + jsr onemhz71 + jsr writeblock + WRT05 - SRCH + + ldy CURRBLOCK + iny + cpy NUMBLOCKS + bcc saveloop + +saverexit: lda #ATNA_OUT | CLK_OUT | DATA_OUT + sta VIA1_PRB + + sei + ldx CURRTRACK71 + txs + +.macro HOOK71 + tsx + stx CURRTRACK71 + ldx #0 +.endmacro + RETURNTOLOADER71 HOOK71 + +rdygetbyte: ldx #0 + lda #ATNA_OUT + sta VIA1_PRB + +getbyte: lda #$ff + sta TIMER + ENABLE_WATCHDOG + DRIVEGETBYTE 1571, getbytecmp + rts + +filetracks = idleloop71 +filesectors = * + + .endscope + +savename71: .res FILENAME_MAXLENGTH + .byte 0 + + .reloc +saver71end: + +saver81: + .org $0594; loadfile81 + + .assert * <= loadfile81, error, "***** 1581 save code above loadfile81 position. *****" + .assert * >= loadfile81, error, "***** 1581 save code below loadfile81 position. *****" + +drvsaver81: .scope cbm1581 + +.include "cia.inc" + +filetracks = $0900 +filesectors = $0a00 +rawblock = $0b00 + +NUMBLOCKS = $18 +CURRBLOCK = $19 +BLOCKSIZE = $1a +SENDVALUE = $1b + +STROBE_CONTROLLER = $ff54 + +OK_DV = $00 +WRTSD_DV = $90 +TRKWRT_DV = $a2 + +FD2K4K_F = $fea4 + +BUFFER0 = $0300 +BUFFERSIZE = $0100 + +BUFFERINDEX = (rawblock - BUFFER0) / BUFFERSIZE + +JOBCODESTABLE = $02 +JOBTRKSCTTABLE = $0b + +TRACKOFFSET = 0 +SECTOROFFSET = 1 + +BUFFERTRACK = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + TRACKOFFSET +BUFFERSECTOR = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + SECTOROFFSET + + bcc aftfindfil + bcs filenotfnd; jmp + + lda #DATA_OUT + sta CIA_PRB + + jsr swapzp81; restore loader zeropage + + lda FD2K4K_F + cmp #'f' + bne notonfd + ldx #fdwatchdge - fdwatchdog - 1 +: lda fdwatchdog,x + sta getbyte,x + dex + bpl :- +notonfd: + ldx #0 + stx NUMBLOCKS + +: lda #CLK_IN + and CIA_PRB + beq :- + lda #CLK_OUT + sta CIA_PRB +: lda CIA_PRB + lsr + bcs :- + + lda #WRITE_PROTECT + and CIA_PRB + beq writeprot + +dosave: lda savename81,x + beq :+ + sta filename81,x + inx + bne dosave +: + sec + jmp findfile81 + +aftfindfil: tya + pha + jsr bsyledon81 + pla + tay + lda DIRTRACKS81,x + bne :+; jmp + +enumblocks: ldx NUMBLOCKS + lda filetracks,x + ldy filesectors,x + jsr getblock81 + bcs enumblocks + + ;lda LINKTRACK + ;ldy LINKSECTOR + inc NUMBLOCKS +: ldx NUMBLOCKS + + cpx #MAXBLOCKS + 1 + bcs :+ + pha + tya + sta filesectors,x + pla + sta filetracks,x + bne enumblocks +: + txa + SKIPWORD +writeprot: lda #WRITE_PROTECTED + SKIPWORD +filenotfnd: lda #diskio::status::FILE_NOT_FOUND; $ff + pha + + jsr initwdog81 + jsr enablwdg81 + + pla + + DRIVESENDBYTE 1581, SENDVALUE + + lda NUMBLOCKS + beq saverexit + + ;ldy #0 +saveloop: sty CURRBLOCK + + jsr enablwdg81 + + jsr rdygetbyte + beq saverexit; branch if file on disk too small/large + sta BLOCKSIZE + + ;ldx #0 +: jsr getbyte + sta rawblock + 2,x + inx + cpx BLOCKSIZE + bcc :- + + lda #CLK_OUT | DATA_OUT + sta CIA_PRB + sei + + lda #0 + beq :++; jmp +: sta rawblock + 2,x + inx +: cpx #$fe + bcc :-- + +retry: ldx CURRBLOCK + ldy filesectors + 1,x + lda filetracks + 1,x + sta rawblock + bne :+ + ldy BLOCKSIZE + iny +: sty rawblock + 1 + + lda filetracks,x + sta BUFFERTRACK + lda filesectors,x + sta BUFFERSECTOR + jsr initcntr81 + lda #WRTSD_DV + ldx #BUFFERINDEX + jsr STROBE_CONTROLLER + + lda JOBCODESTABLE + BUFFERINDEX; FD does not return the error status in the accu + cmp #OK_DV + 1 + bcs retry + + ldy CURRBLOCK + iny + cpy NUMBLOCKS + bcc saveloop + +: lda #TRKWRT_DV + ldx #BUFFERINDEX + jsr STROBE_CONTROLLER + + lda JOBCODESTABLE + BUFFERINDEX; FD does not return the error status in the accu + cmp #OK_DV + 1 + bcs :- + +saverexit: lda #CLK_OUT | DATA_OUT + sta CIA_PRB + sei + +zpbuffer = returnrun + returnend - return + + ldx #returnend - return - 1 +: lda return,x + sta returnrun,x + lda $00,x + sta zpbuffer,x; buffer zeropage, as it will be overwritten with pre-loader system zeropage + dex + bpl :- + + jmp returnrun +return: + .org rawblock + +.macro HOOK81 + ldx #returnend - return - 1 +: lda zpbuffer,x + sta $00,x; restore zeropage + dex + bpl :- + + jsr swapzp81 +.endmacro + +returnrun: RETURNTOLOADER81 HOOK81 + + .org * - returnrun + return +returnend: + +rdygetbyte: ldx #0 + stx CIA_PRB + +getbyte: lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START + sta CIA_CRB; reset watchdog time-out + + DRIVEGETBYTE 1581 + rts + +fdwatchdog: lda #$ff + sta $4005; VIA_T1C_H +fdwatchdge: + .endscope + +savename81: .res FILENAME_MAXLENGTH + .byte 0 + + .assert * <= DRVCODEND81, error, "***** 1581 save code too large. *****" + + .reloc +saver81end: + +.endif; ONLY_1541_AND_COMPATIBLE + +.if LOAD_VIA_KERNAL_FALLBACK + +kernalsave: stx namestrpos + sty namestrpos + 1 + ldx #0 +namestrpos = * + 1 +: lda $ffff,x + beq :+ + sta savename + 2,x + inx + cpx #FILENAME_MAXLENGTH + bcc :- +: stx FNLEN + + lda #KERNALFILENO + ldx FA + ldy #$00 + jsr SETLFS + lda FNLEN + ldx #.lobyte(savename + 2) + ldy #.hibyte(savename + 2) + jsr SETNAM + jsr OPEN + bcs jmpsaveerr + + ldx LA + jsr CHKIN + + ldx #0 + ldy #0 +kernalenum: lda STATUS + bne kernlenumd + + .if KERNAL_FALLBACK_SEI_WORKAROUNDS + BRANCH_IF_DRIVE_PARALLEL :++ +: BRANCH_IF_BLOCK_NOT_READY :- +: + .endif; KERNAL_FALLBACK_SEI_WORKAROUNDS + jsr BASIN + lda STATUS + bne kernlenumd + inx + bne kernalenum + iny + bne kernalenum +kernlenumd: cmp #KERNAL_STATUS_EOF + bne jmpsaveerr + + lda #diskio::status::FILE_NOT_FOUND + cpx #0 + bne :+ + cpy #0 + bne :+ +jmpsaveerr: jmp saveerror +: lda #0 + sta BYTECOUNT + clc +: inc BYTECOUNT + bne :+ + dec BYTECOUNT +: txa + sbc #$fe + tax + tya + sbc #0 + tay + bcs :-- + + lda BYTECOUNT + cmp BLOCKCNT + beq :+ + + lda #diskio::status::FILE_ON_DISK_TOO_SMALL + bcc jmpsaveerr + lda #diskio::status::FILE_ON_DISK_TOO_LARGE + bcs jmpsaveerr; jmp +: + jsr KERNLCLOSE + + lda #KERNALFILENO + ldx FA + ldy #$01 + jsr SETLFS + clc + lda #2 + adc FNLEN + ldx #.lobyte(savename) + ldy #.hibyte(savename) + jsr SETNAM + jsr OPEN + bcs saveerror + + ldx LA + jsr CKOUT + lda #diskio::status::GENERIC_KERNAL_ERROR + ldx STATUS + bne saveerror + + ldy #saveparams::loadaddress + lda (PARAMSPTR),y + jsr BSOUT + iny + lda (PARAMSPTR),y + jsr BSOUT + + lda #diskio::status::WRITE_PROTECT_ON + ldx STATUS + bne saveerror + + sec + lda LENGTH + sbc #2 + sta LENGTH + bcs :+ + dec LENGTH + 1 +: + ldy #saveparams::from + lda (PARAMSPTR),y + sta SAVEPTR + iny + lda (PARAMSPTR),y + sta SAVEPTR + 1 + +krnlsavelp: lda #diskio::status::GENERIC_KERNAL_ERROR + ldx STATUS + bne saveerror + + ldy #0 +.if SAVE_FROM_RAM_UNDER_IO + ENABLE_ALL_RAM_X + lda (SAVEPTR),y + ENABLE_KERNAL_SERIAL_ROUTINES_Y +.else + lda (SAVEPTR),y +.endif + jsr BSOUT + inc SAVEPTR + bne :+ + inc SAVEPTR + 1 +: sec + lda LENGTH + sbc #1 + sta LENGTH + lda LENGTH + 1 + sbc #0 + bcc :+ + sta LENGTH + 1 + ora LENGTH + bne krnlsavelp +: + lda #diskio::status::OK + +saveerror: pha + txa + pha + jsr KERNLCLOSE + pla + tax + pla + cmp #diskio::status::OK + 1 + rts + +savename: .byte "@:" + .res FILENAME_MAXLENGTH + +.endif; LOAD_VIA_KERNAL_FALLBACK + +swapparams: .word 0; buffer + + .word cbm1541::saveentry; entry + .word drvsaver41; to + .word saver41end - saver41; length + .word saver41; from + +.if ONLY_1541_AND_COMPATIBLE = 0 + + .word drvsaver71 + 5; entry + .word drvsaver71; to + .word saver71end - saver71; length + .word saver71; from + + .word drvsaver81 + 4; entry + .word drvsaver81; to + .word saver81end - saver81; length + .word saver81; from + +.endif; ONLY_1541_AND_COMPATIBLE diff --git a/loader/tools/b2/Decruncher.inc b/loader/tools/b2/Decruncher.inc new file mode 100644 index 0000000..9845fc3 --- /dev/null +++ b/loader/tools/b2/Decruncher.inc @@ -0,0 +1,161 @@ +; ByteBoozer Decruncher /HCL May.2003 +; B2 Decruncher December 2014 + +; call: Y = AddrLo +; X = AddrHi + +;Variables.. #Bytes +zp_base = $02 ; - +bits = zp_base ;1 +put = zp_base+2 ;2 + +#macro GetNextBit() {.( + asl bits + bne DgEnd + jsr GetNewBits +DgEnd +.)} + +#macro GetLen() {.( + lda #1 +GlLoop + .GetNextBit() + bcc GlEnd + .GetNextBit() + rol + bpl GlLoop +GlEnd +.)} + +Decrunch + sty Get1+1 + sty Get2+1 + sty Get3+1 + stx Get1+2 + stx Get2+2 + stx Get3+2 + + ldx #0 + jsr GetNewBits + sty put-1,x + cpx #2 + bcc *-7 + lda #$80 + sta bits +DLoop + .GetNextBit() + bcs Match +Literal + ; Literal run.. get length. + .GetLen() + sta LLen+1 + + ldy #0 +LLoop +Get3 lda $feed,x + inx + bne *+5 + jsr GnbInc +L1 sta (put),y + iny +LLen cpy #0 + bne LLoop + + clc + tya + adc put + sta put + bcc *+4 + inc put+1 + + iny + beq DLoop + + ; Has to continue with a match.. + +Match + ; Match.. get length. + .GetLen() + sta MLen+1 + + ; Length 255 -> EOF + cmp #$ff + beq End + + ; Get num bits + cmp #2 + lda #0 + rol + .GetNextBit() + rol + .GetNextBit() + rol + tay + lda Tab,y + beq M8 + + ; Get bits < 8 +M_1 .GetNextBit() + rol + bcs M_1 + bmi MShort +M8 + ; Get byte + eor #$ff + tay +Get2 lda $feed,x + inx + bne *+5 + jsr GnbInc + jmp Mdone +MShort + ldy #$ff +Mdone + ;clc + adc put + sta MLda+1 + tya + adc put+1 + sta MLda+2 + + ldy #$ff +MLoop iny +MLda lda $beef,y + sta (put),y +MLen cpy #0 + bne MLoop + + ;sec + tya + adc put + sta put + bcc *+4 + inc put+1 + + jmp DLoop + +End rts + +GetNewBits +Get1 ldy $feed,x + sty bits + rol bits + inx + bne GnbEnd +GnbInc inc Get1+2 + inc Get2+2 + inc Get3+2 +GnbEnd + rts + +Tab + ; Short offsets + .byte %11011111 ; 3 + .byte %11111011 ; 6 + .byte %00000000 ; 8 + .byte %10000000 ; 10 + ; Long offsets + .byte %11101111 ; 4 + .byte %11111101 ; 7 + .byte %10000000 ; 10 + .byte %11110000 ; 13 diff --git a/loader/tools/b2/Makefile b/loader/tools/b2/Makefile new file mode 100644 index 0000000..6be80e2 --- /dev/null +++ b/loader/tools/b2/Makefile @@ -0,0 +1,9 @@ +CC = gcc + +OBJECTS = \ + file.c \ + cruncher.c \ + bb.c + +all: $(OBJECTS) + $(CC) -O3 $(OBJECTS) -o b2.exe diff --git a/loader/tools/b2/bb.c b/loader/tools/b2/bb.c new file mode 100644 index 0000000..226b82f --- /dev/null +++ b/loader/tools/b2/bb.c @@ -0,0 +1,77 @@ +#include "bb.h" +#include "file.h" +#include "cruncher.h" +#include +#include + +int main(int argc, char * argv[]) +{ + File myFile; + File myBBFile; + char* fileName; + bool isExecutable = false; + bool isRelocated = false; + uint address = 0; + + if((argc != 2 && argc != 4) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "-help") == 0)){ + printf("Usage: b2 [-[c|e|r] xxxx] \n"); + printf(" -c: Make executable with start address xxxx.\n"); + printf(" -e: Same as -c :P.\n"); + printf(" -r: Relocate file to hex address xxxx.\n"); + return 0; + } + + if(argc == 2) { + fileName = argv[1]; + } else { + int i; + char *s = argv[2]; + fileName = argv[3]; + + if((strcmp(argv[1], "-c") == 0) || + (strcmp(argv[1], "-e") == 0)) + isExecutable = true; + else if(strcmp(argv[1], "-r") == 0) + isRelocated = true; + else { + printf("Don't understand, aborting.\n"); + return -1; + } + if(strlen(s) != 4){ + printf("Don't understand, aborting.\n"); + return -1; + } + + for(i = 0; i < 4; ++i){ + byte c; + if(s[i] >= '0' && s[i] <= '9') c = s[i] - '0'; + if(s[i] >= 'a' && s[i] <= 'f') c = s[i] - 'a' + 10; + if(s[i] >= 'A' && s[i] <= 'F') c = s[i] - 'A' + 10; + address *= 16; + address += c; + } + } + + if(!readFile(&myFile, fileName)) { + printf("Error (B-1): Open file \"%s\", aborting.\n", fileName); + return -1; + } + + if(!crunch(&myFile, &myBBFile, address, isExecutable, isRelocated)) { + freeFile(&myFile); + return -1; + } + + if(!writeFile(&myBBFile, myFile.name)) { + printf("Error (B-2): Write file \"%s\", aborting.\n", myBBFile.name); + } + + printf("B2: \"%s\" -> \"%s\"\n", myFile.name, myBBFile.name); + + freeFile(&myFile); + freeFile(&myBBFile); + + return 0; +} diff --git a/loader/tools/b2/bb.h b/loader/tools/b2/bb.h new file mode 100644 index 0000000..3da135d --- /dev/null +++ b/loader/tools/b2/bb.h @@ -0,0 +1,19 @@ +#ifndef _bb_h_ +#define _bb_h_ + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef byte +typedef unsigned char byte; +#endif +#ifndef uint +typedef unsigned int uint; +#endif + +typedef enum { false = 0, true = 1 } bool; + +#define memSize 65536 + +#endif // _bb_h_ diff --git a/loader/tools/b2/cruncher.c b/loader/tools/b2/cruncher.c new file mode 100644 index 0000000..e5c7a1d --- /dev/null +++ b/loader/tools/b2/cruncher.c @@ -0,0 +1,747 @@ +#include "cruncher.h" +#include +#include + +#define log(format, ...) +//#define log(format, ...) fprintf (stderr, format, ## __VA_ARGS__) + +#define NUM_BITS_SHORT_0 3 +#define NUM_BITS_SHORT_1 6 +#define NUM_BITS_SHORT_2 8 +#define NUM_BITS_SHORT_3 10 +#define NUM_BITS_LONG_0 4 +#define NUM_BITS_LONG_1 7 +#define NUM_BITS_LONG_2 10 +#define NUM_BITS_LONG_3 13 + +#define LEN_SHORT_0 (1 << NUM_BITS_SHORT_0) +#define LEN_SHORT_1 (1 << NUM_BITS_SHORT_1) +#define LEN_SHORT_2 (1 << NUM_BITS_SHORT_2) +#define LEN_SHORT_3 (1 << NUM_BITS_SHORT_3) +#define LEN_LONG_0 (1 << NUM_BITS_LONG_0) +#define LEN_LONG_1 (1 << NUM_BITS_LONG_1) +#define LEN_LONG_2 (1 << NUM_BITS_LONG_2) +#define LEN_LONG_3 (1 << NUM_BITS_LONG_3) + +#define COND_SHORT_0(o) ((o) >= 0 && (o) < LEN_SHORT_0) +#define COND_SHORT_1(o) ((o) >= LEN_SHORT_0 && (o) < LEN_SHORT_1) +#define COND_SHORT_2(o) ((o) >= LEN_SHORT_1 && (o) < LEN_SHORT_2) +#define COND_SHORT_3(o) ((o) >= LEN_SHORT_2 && (o) < LEN_SHORT_3) +#define COND_LONG_0(o) ((o) >= 0 && (o) < LEN_LONG_0) +#define COND_LONG_1(o) ((o) >= LEN_LONG_0 && (o) < LEN_LONG_1) +#define COND_LONG_2(o) ((o) >= LEN_LONG_1 && (o) < LEN_LONG_2) +#define COND_LONG_3(o) ((o) >= LEN_LONG_2 && (o) < LEN_LONG_3) + +#define MAX_OFFSET LEN_LONG_3 +#define MAX_OFFSET_SHORT LEN_SHORT_3 + +#define DECRUNCHER_LENGTH 0xd5 +byte decrCode[DECRUNCHER_LENGTH] = { + 0x0b, 0x08, 0x00, 0x00, 0x9e, 0x32, 0x30, 0x36, 0x31, 0x00, 0x00, 0x00, 0x78, 0xa9, 0x34, 0x85, + 0x01, 0xa2, 0xb7, 0xbd, 0x1e, 0x08, 0x95, 0x0f, 0xca, 0xd0, 0xf8, 0x4c, 0x10, 0x00, 0xbd, 0xd6, + 0x07, 0x9d, 0x00, 0xff, 0xe8, 0xd0, 0xf7, 0xc6, 0x12, 0xc6, 0x15, 0xa5, 0x12, 0xc9, 0x07, 0xb0, + 0xed, 0x20, 0xa0, 0x00, 0xb0, 0x17, 0x20, 0x8e, 0x00, 0x85, 0x36, 0xa0, 0x00, 0x20, 0xad, 0x00, + 0x91, 0x77, 0xc8, 0xc0, 0x00, 0xd0, 0xf6, 0x20, 0x83, 0x00, 0xc8, 0xf0, 0xe4, 0x20, 0x8e, 0x00, + 0xaa, 0xe8, 0xf0, 0x71, 0x86, 0x7b, 0xa9, 0x00, 0xe0, 0x03, 0x2a, 0x20, 0x9b, 0x00, 0x20, 0x9b, + 0x00, 0xaa, 0xb5, 0xbf, 0xf0, 0x07, 0x20, 0x9b, 0x00, 0xb0, 0xfb, 0x30, 0x07, 0x49, 0xff, 0xa8, + 0x20, 0xad, 0x00, 0xae, 0xa0, 0xff, 0x65, 0x77, 0x85, 0x74, 0x98, 0x65, 0x78, 0x85, 0x75, 0xa0, + 0x00, 0xb9, 0xad, 0xde, 0x99, 0x00, 0x00, 0xc8, 0xc0, 0x00, 0xd0, 0xf5, 0x20, 0x83, 0x00, 0xd0, + 0xa0, 0x18, 0x98, 0x65, 0x77, 0x85, 0x77, 0x90, 0x02, 0xe6, 0x78, 0x60, 0xa9, 0x01, 0x20, 0xa0, + 0x00, 0x90, 0x05, 0x20, 0x9b, 0x00, 0x10, 0xf6, 0x60, 0x20, 0xa0, 0x00, 0x2a, 0x60, 0x06, 0xbe, + 0xd0, 0x08, 0x48, 0x20, 0xad, 0x00, 0x2a, 0x85, 0xbe, 0x68, 0x60, 0xad, 0xed, 0xfe, 0xe6, 0xae, + 0xd0, 0x02, 0xe6, 0xaf, 0x60, 0xa9, 0x37, 0x85, 0x01, 0x4c, 0x00, 0x00, 0x80, 0xdf, 0xfb, 0x00, + 0x80, 0xef, 0xfd, 0x80, 0xf0 +}; + + +byte *ibuf; +byte *obuf; +uint ibufSize; +int get; //points to ibuf[] +uint put; //points to obuf[] + +typedef struct { + uint cost; + uint next; + uint litLen; + uint offset; +} node; + +typedef struct { + byte value; + byte valueAfter; + uint length; +} RLEInfo; + +node *context; +uint *link; +RLEInfo *rleInfo; +uint first[65536]; +uint last[65536]; + +byte curByte; +byte curCnt; +uint curIndex; + +void wBit(uint bit) { + if(curCnt == 0) { + obuf[curIndex] = curByte; + curIndex = put; + curCnt = 8; + curByte = 0; + put++; + } + + curByte <<= 1; + curByte |= (bit & 1); + curCnt--; +} + +void wFlush() { + while(curCnt != 0) { + curByte <<= 1; + curCnt--; + } + obuf[curIndex] = curByte; +} + +void wByte(uint b) { + obuf[put] = b; + put++; +} + +void wBytes(uint get, uint len) { + uint i; + for(i = 0; i < len; i++) { + wByte(ibuf[get]); + get++; + } +} + +void wLength(uint len) { +// if(len == 0) return; // Should never happen + + byte bit = 0x80; + while((len & bit) == 0) { + bit >>= 1; + } + + while(bit > 1) { + wBit(1); + bit >>= 1; + wBit(((len & bit) == 0) ? 0 : 1); + } + + if(len < 0x80) { + wBit(0); + } +} + +void wOffset(uint offset, uint len) { + uint i = 0; + uint n = 0; + uint b; + + if(len == 1) { + if(COND_SHORT_0(offset)) { + i = 0; + n = NUM_BITS_SHORT_0; + } + if(COND_SHORT_1(offset)) { + i = 1; + n = NUM_BITS_SHORT_1; + } + if(COND_SHORT_2(offset)) { + i = 2; + n = NUM_BITS_SHORT_2; + } + if(COND_SHORT_3(offset)) { + i = 3; + n = NUM_BITS_SHORT_3; + } + } else { + if(COND_LONG_0(offset)) { + i = 0; + n = NUM_BITS_LONG_0; + } + if(COND_LONG_1(offset)) { + i = 1; + n = NUM_BITS_LONG_1; + } + if(COND_LONG_2(offset)) { + i = 2; + n = NUM_BITS_LONG_2; + } + if(COND_LONG_3(offset)) { + i = 3; + n = NUM_BITS_LONG_3; + } + } + + // First write number of bits + wBit(((i & 2) == 0) ? 0 : 1); + wBit(((i & 1) == 0) ? 0 : 1); + + if(n >= 8) { // Offset is 2 bytes + + // Then write the bits less than 8 + b = 1 << n; + while(b > 0x100) { + b >>= 1; + wBit(((b & offset) == 0) ? 0 : 1); + }; + + // Finally write a whole byte, if necessary + wByte((offset & 255) ^ 255); // Inverted(!) + offset >>= 8; + + } else { // Offset is 1 byte + + // Then write the bits less than 8 + b = 1 << n; + while(b > 1) { + b >>= 1; + wBit(((b & offset) == 0) ? 1 : 0); // Inverted(!) + }; + + } +} + + +/* + * Cost functions + */ +uint costOfLength(uint len) { + if(len == 1) return 1; + if(len >= 2 && len <= 3) return 3; + if(len >= 4 && len <= 7) return 5; + if(len >= 8 && len <= 15) return 7; + if(len >= 16 && len <= 31) return 9; + if(len >= 32 && len <= 63) return 11; + if(len >= 64 && len <= 127) return 13; + if(len >= 128 && len <= 255) return 14; + + printf("costOfLength got wrong value: %i\n", len); + return 10000; +} + +uint costOfOffset(uint offset, uint len) { + if(len == 1) { + if(COND_SHORT_0(offset)) return NUM_BITS_SHORT_0; + if(COND_SHORT_1(offset)) return NUM_BITS_SHORT_1; + if(COND_SHORT_2(offset)) return NUM_BITS_SHORT_2; + if(COND_SHORT_3(offset)) return NUM_BITS_SHORT_3; + } else { + if(COND_LONG_0(offset)) return NUM_BITS_LONG_0; + if(COND_LONG_1(offset)) return NUM_BITS_LONG_1; + if(COND_LONG_2(offset)) return NUM_BITS_LONG_2; + if(COND_LONG_3(offset)) return NUM_BITS_LONG_3; + } + + printf("costOfOffset got wrong offset: %i\n", offset); + return 10000; +} + +uint calculateCostOfMatch(uint len, uint offset) { + uint cost = 1; // Copy-bit + cost += costOfLength(len - 1); + cost += 2; // NumOffsetBits + cost += costOfOffset(offset - 1, len - 1); + return cost; +} + +uint calculateCostOfLiteral(uint oldCost, uint litLen) { + uint newCost = oldCost + 8; + + // FIXME, what if litLen > 255? + // + // FIXME, cost model for literals does not work. + // Quick wins on short matches are prioritized before + // a longer literal run, which in the end results in a + // worse result. + // Most obvious on files hard to crunch. + switch(litLen) { + case 1: + case 128: + newCost++; + break; + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + newCost += 2; + break; + default: + break; + } + + return newCost; +} + + +void setupHelpStructures() { + uint i; + + // Setup RLE-info + get = ibufSize - 1; + while (get > 0) { + + byte cur = ibuf[get]; + if (cur == ibuf[get-1]) { + + uint len = 2; + while ((get >= len) && + (cur == ibuf[get-len])) { + len++; + } + + rleInfo[get].length = len; + if (get >= len) { + rleInfo[get].valueAfter = ibuf[get-len]; + } else { + rleInfo[get].valueAfter = cur; // Avoid accessing ibuf[-1] + } + + get -= len; + } else { + get--; + } + } + + + // Setup Linked list + for (i = 0; i < 65536; i++) { + first[i] = 0; + last[i] = 0; + } + + get = ibufSize - 1; + uint cur = ibuf[get]; + + while (get > 0) { + + cur = ((cur << 8) | ibuf[get-1]) & 65535; + + if (first[cur] == 0) { + first[cur] = last[cur] = get; + } else { + link[last[cur]] = get; + last[cur] = get; + } + + if (rleInfo[get].length == 0) { // No RLE-match here.. + get--; + } else { // if RLE-match.. + get -= (rleInfo[get].length - 1); + } + + } +} + + +void findMatches() { + + typedef struct match { + uint length; + uint offset; + } match; + + match matches[256]; + + node lastNode; + uint i; + + get = ibufSize - 1; + uint cur = ibuf[get]; + + lastNode.cost = 0; + lastNode.next = 0; + lastNode.litLen = 0; + + while (get >= 0) { + + // Clear matches for current position + for (i = 0; i < 256; i++) { + matches[i].length = 0; + matches[i].offset = 0; + } + + cur = (cur << 8) & 65535; // Table65536 lookup + if (get > 0) cur |= ibuf[get-1]; + int scn = first[cur]; + scn = link[scn]; + + uint longestMatch = 0; + + if (rleInfo[get].length == 0) { // No RLE-match here.. + + // Scan until start of file, or max offset + while (((get - scn) <= MAX_OFFSET) && + (scn > 0) && + (longestMatch < 255)) { + + // Ok, we have a match of length 2 + // ..or longer, but max 255 or file start + uint len = 2; + while ((len < 255) && + (scn >= len) && + (ibuf[scn - len] == ibuf[get - len])) { + ++len; + } + + // Calc offset + uint offset = get - scn; + + // Store match only if it's the longest so far + if(len > longestMatch) { + longestMatch = len; + + // Store the match only if first (= best) of this length + while(len >= 2 && matches[len].length == 0) { + + // If len == 2, check against short offset!! + if ((len > 2) || + ((len == 2) && (offset <= MAX_OFFSET_SHORT))) { + matches[len].length = len; + matches[len].offset = get - scn; + } + + len--; + }; + } + + scn = link[scn]; // Table65535 lookup + }; + + first[cur] = link[first[cur]]; // Waste first entry + + } else { // if RLE-match.. + + uint rleLen = rleInfo[get].length; + byte rleValAfter = rleInfo[get].valueAfter; + + + // First match with self-RLE, which is always + // one byte shorter than the RLE itself. + uint len = rleLen - 1; + if (len > 1) { + if (len > 255) len = 255; + longestMatch = len; + + // Store the match + while(len >= 2) { + matches[len].length = len; + matches[len].offset = 1; + + len--; + }; + } + + + // Search for more RLE-matches.. + // Scan until start of file, or max offset + while (((get - scn) <= MAX_OFFSET) && + (scn > 0) && + (longestMatch < 255)) { + + // Check for longer matches with same value and after.. + // FIXME, that is not what it does, is it?! + if ((rleInfo[scn].length > longestMatch) && + (rleLen > longestMatch)) { + + uint offset = get - scn; + len = rleInfo[scn].length; + + if (len > rleLen) + len = rleLen; + + if ((len > 2) || + ((len == 2) && (offset <= MAX_OFFSET_SHORT))) { + matches[len].length = len; + matches[len].offset = offset; + + longestMatch = len; + } + } + + + // Check for matches beyond the RLE.. + if ((rleInfo[scn].length >= rleLen) && + (rleInfo[scn].valueAfter == rleValAfter)) { + + // Here is a match that goes beyond the RLE.. + // Find out correct offset to use valueAfter.. + // Then search further to see if more bytes equal. + + len = rleLen; + uint offset = get - scn + (rleInfo[scn].length - rleLen); + + if (offset <= MAX_OFFSET) { + while ((len < 255) && + (get >= (offset + len)) && + (ibuf[get - (offset + len)] == ibuf[get - len])) { + ++len; + } + if (len > longestMatch){ + longestMatch = len; + + // Store the match only if first (= best) of this length + while(len >= 2 && matches[len].length == 0) { + + // If len == 2, check against short offset!! + if ((len > 2) || + ((len == 2) && (offset <= MAX_OFFSET_SHORT))) { + matches[len].length = len; + matches[len].offset = offset; + } + + len--; + }; //while + } + } + } + + scn = link[scn]; // Table65535 lookup + } + + + if (rleInfo[get].length > 2) { + // Expand RLE to next position + rleInfo[get-1].length = rleInfo[get].length - 1; + rleInfo[get-1].value = rleInfo[get].value; + rleInfo[get-1].valueAfter = rleInfo[get].valueAfter; + } else { + // End of RLE, advance link. + first[cur] = link[first[cur]]; // Waste first entry + } + } + + + // Now we have all matches from this position.. + // ..visit all nodes reached by the matches. + + for (i = 255; i > 0; i--) { + + // Find all matches we stored + uint len = matches[i].length; + uint offset = matches[i].offset; + + if (len != 0) { + + uint targetI = get - len + 1; + node* target = &context[targetI]; + + // Calculate cost for this jump + uint currentCost = lastNode.cost; + currentCost += calculateCostOfMatch(len, offset); + + // If this match is first or cheapest way to get here + // then update node + if (target->cost == 0 || + target->cost > currentCost) { + + target->cost = currentCost; + target->next = get + 1; + target->litLen = 0; + target->offset = offset; + } + } + } + + + // Calc the cost for this node if using one more literal + uint litLen = lastNode.litLen + 1; + uint litCost = calculateCostOfLiteral(lastNode.cost, litLen); + + // If literal run is first or cheapest way to get here + // then update node + node* this = &context[get]; + if (this->cost == 0 || + this->cost >= litCost) { + this->cost = litCost; + this->next = get + 1; + this->litLen = litLen; + } + + lastNode.cost = this->cost; + lastNode.next = this->next; + lastNode.litLen = this->litLen; + + // Loop to the next position + get--; + }; + +} + + +// Returns margin +int writeOutput() { + uint i; + + put = 0; + + curByte = 0; + curCnt = 8; + curIndex = put; + put++; + + int maxDiff = 0; + + bool needCopyBit = true; + + for (i = 0; i < ibufSize;) { + + uint link = context[i].next; + uint cost = context[i].cost; + uint litLen = context[i].litLen; + uint offset = context[i].offset; + + if (litLen == 0) { + // Put Match + uint len = link - i; + + log("$%04x: Mat(%i, %i)\n", i, len, offset); + + if(needCopyBit) { + wBit(1); + } + wLength(len - 1); + wOffset(offset - 1, len - 1); + + i = link; + + needCopyBit = true; + } else { + // Put LiteralRun + needCopyBit = false; + + while(litLen > 0) { + uint len = litLen < 255 ? litLen : 255; + + log("$%04x: Lit(%i)\n", i, len); + + wBit(0); + wLength(len); + wBytes(i, len); + + if (litLen == 255) { + needCopyBit = true; + } + + litLen -= len; + i += len; + }; + } + + if ((int)(i - put) > maxDiff) { + maxDiff = i - put; + } + + } + + wBit(1); + wLength(0xff); + wFlush(); + + int margin = (maxDiff - (i - put)); + + return margin; +} + + +bool crunch(File *aSource, + File *aTarget, + uint address, + bool isExecutable, + bool isRelocated) +{ + uint i; + byte *target; + + ibufSize = aSource->size - 2; + ibuf = (byte*)malloc(ibufSize); + context = (node*)malloc(sizeof(node) * ibufSize); + link = (uint*)malloc(sizeof(uint) * ibufSize); + rleInfo = (RLEInfo*)malloc(sizeof(RLEInfo) * ibufSize); + + // Load ibuf and clear context + for(i = 0; i < ibufSize; ++i) { + ibuf[i] = aSource->data[i + 2]; + context[i].cost = 0; + link[i] = 0; + rleInfo[i].length = 0; + } + + setupHelpStructures(); + findMatches(); + obuf = (byte*)malloc(memSize); + int margin = writeOutput(); + + uint packLen = put; + uint fileLen = put; + uint decrLen = 0; + if(isExecutable) { + decrLen = DECRUNCHER_LENGTH; + fileLen += decrLen + 2; + } else { + fileLen += 4; + } + + aTarget->size = fileLen; + aTarget->data = (byte*)malloc(aTarget->size); + target = aTarget->data; + + if(isExecutable) { + uint startAddress = 0x10000 - packLen; + uint transfAddress = fileLen + 0x6ff; + + decrCode[0x1f] = transfAddress & 0xff; // Transfer from.. + decrCode[0x20] = transfAddress >> 8; // + decrCode[0xbc] = startAddress & 0xff; // Depack from.. + decrCode[0xbd] = startAddress >> 8; // + decrCode[0x85] = aSource->data[0]; // Depack to.. + decrCode[0x86] = aSource->data[1]; // + decrCode[0xca] = address & 0xff; // Jump to.. + decrCode[0xcb] = address >> 8; // + + target[0] = 0x01; + target[1] = 0x08; + + for(i = 0; i < decrLen; ++i) { + target[i + 2] = decrCode[i]; + } + + for(i = 0; i < put; ++i) { + target[i + 2 + decrLen] = obuf[i]; + } + + } else { // Not executable.. + + // Experimantal decision of start address +// uint startAddress = 0xfffa - packLen - 2; + uint startAddress = (aSource->data[1] << 8) | aSource->data[0]; + startAddress += (ibufSize - packLen - 2 + margin); + + if (isRelocated) { + startAddress = address - packLen - 2; + } + + target[0] = startAddress & 0xff; // Load address + target[1] = startAddress >> 8; + target[2] = aSource->data[0]; // Depack to address + target[3] = aSource->data[1]; + + for(i = 0; i < put; ++i) { + target[i + 4] = obuf[i]; + } + } + + free(ibuf); + free(context); + free(link); + free(rleInfo); + + return true; +} diff --git a/loader/tools/b2/cruncher.h b/loader/tools/b2/cruncher.h new file mode 100644 index 0000000..c7bfb5f --- /dev/null +++ b/loader/tools/b2/cruncher.h @@ -0,0 +1,9 @@ +#ifndef _cruncher_h_ +#define _cruncher_h_ + +#include "bb.h" +#include "file.h" + +bool crunch(File *aSource, File *aTarget, uint startAdress, uint decrFlag, bool isRelocated); + +#endif // _cruncher_h_ diff --git a/loader/tools/b2/file.c b/loader/tools/b2/file.c new file mode 100644 index 0000000..f76d774 --- /dev/null +++ b/loader/tools/b2/file.c @@ -0,0 +1,72 @@ +#include "file.h" +#include +#include + +void freeFile(File *aFile) +{ + free(aFile->name); + free(aFile->data); +} + +bool readFile(File *aFile, const char *fileName) +{ + FILE *fp = NULL; + struct stat fileStatus; + + aFile->name = (char *)strdup(fileName); + + if(stat(aFile->name, &fileStatus) == -1) { + return false; + } + aFile->size = fileStatus.st_size; + + fp = fopen(aFile->name, "rb"); + if(fp == NULL) { + return false; + } + + aFile->data = (byte *)malloc(aFile->size); + if(aFile->data == NULL) { + fclose(fp); + return false; + } + + if(fread(aFile->data, 1, aFile->size, fp) != aFile->size) { + fclose(fp); + free(aFile->data); + return false; + } + + fclose(fp); + return true; +} + +bool writeFile(File *aFile, const char *fileName) +{ + FILE *fp = NULL; + size_t length; + char *ext; + + length = strlen(fileName); + aFile->name = (char *)malloc(length + 4); + + if(aFile->name == NULL){ + return false; + } + + strncpy(aFile->name, fileName, length); + strncpy(aFile->name + length, ".b2\0", 4); + + fp = fopen(aFile->name, "wb"); + if(fp == NULL) { + return false; + } + + if(fwrite(aFile->data, 1, aFile->size, fp) != aFile->size) { + fclose(fp); + return false; + } + + fclose(fp); + return true; +} diff --git a/loader/tools/b2/file.h b/loader/tools/b2/file.h new file mode 100644 index 0000000..f7be139 --- /dev/null +++ b/loader/tools/b2/file.h @@ -0,0 +1,19 @@ +#ifndef _file_h_ +#define _file_h_ + +#include "bb.h" + +#include +#include + +typedef struct { + char *name; + size_t size; + byte *data; +} File; + +void freeFile(File *aFile); +bool readFile(File *aFile, const char *fileName); +bool writeFile(File *aFile, const char *fileName); + +#endif // _file_h_ diff --git a/loader/tools/bitnax-07a8c67/lz.c b/loader/tools/bitnax-07a8c67/lz.c new file mode 100644 index 0000000..bcc63a6 --- /dev/null +++ b/loader/tools/bitnax-07a8c67/lz.c @@ -0,0 +1,1887 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + false, + true +} bool; + +#define VERIFY_COST_MODEL 0 +#define DEFAULT_LENGTHS "3/6/8/10:4/7/10/13" +#define BITFIRE_WITH_MOFF 0 + +#define OUTPUT_NONE 0 +#define OUTPUT_SFX 2 +#define OUTPUT_LEVEL 3 +#define OUTPUT_BITFIRE 4 + +enum { + RUN_LIMIT = 0x100, + OFFSET_LENGTH_LIMIT = 15 +}; + +enum { + INFINITE_WINDOW = (unsigned) INT_MIN +}; + +static const char decruncher[] = { +0x01,0x08,0x1c,0x08,0x00,0x00,0x9e,0x32,0x30,0x37,0x38,0x3a,0x22,0x14,0x14,0x14, +0x14,0x14,0x14,0x14,0x14,0x14,0x42,0x49,0x54,0x4e,0x41,0x58,0x00,0x00,0x00,0x78, +0xe6,0x01,0xa2,0x00,0xbd,0x46,0x08,0x9d,0x20,0x00,0xe8,0xd0,0xf7,0xa0,0x01,0xca, +0xbd,0x2c,0x08,0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x31,0x08,0xce,0x34,0x08,0x88, +0xd0,0xed,0xa2,0x1a,0x4c,0x20,0x00,0x38,0x20,0xef,0x00,0x90,0x3b,0xf0,0xf9,0xa9, +0x00,0x2a,0x06,0xee,0xd0,0x03,0x20,0xef,0x00,0x90,0x09,0x06,0xee,0xd0,0xf2,0x20, +0xef,0x00,0xd0,0xed,0x85,0x4f,0xa0,0x00,0xbd,0x00,0x00,0xe8,0xd0,0x03,0x20,0xf9, +0x00,0x99,0x00,0x38,0xc8,0xc0,0x00,0xd0,0xef,0x98,0xd0,0x03,0x4c,0xe7,0x00,0x18, +0x65,0x4b,0x85,0x4b,0x90,0x02,0xe6,0x4c,0xa9,0x01,0x06,0xee,0xd0,0x03,0x20,0xef, +0x00,0xb0,0x17,0x06,0xee,0xd0,0x03,0x20,0xef,0x00,0x90,0x0c,0x06,0xee,0xd0,0x03, +0x20,0xef,0x00,0x2a,0x90,0xed,0xb0,0x7f,0x69,0x01,0x85,0xdd,0xa9,0x20,0x2a,0x06, +0xee,0xd0,0x03,0x20,0xef,0x00,0x2a,0x90,0xf6,0xa8,0xb9,0x08,0x01,0xf0,0x10,0x06, +0xee,0xd0,0x07,0x84,0xa2,0x20,0xef,0x00,0xa0,0x00,0x2a,0x90,0xf2,0x30,0x1f,0x85, +0xc0,0xbd,0x00,0x00,0xe8,0xd0,0x03,0x20,0xf9,0x00,0x79,0x10,0x01,0xb0,0x03,0xc6, +0xc0,0x38,0x65,0x4b,0x85,0xd8,0xa9,0x00,0x79,0x18,0x01,0x38,0xb0,0x09,0x79,0x10, +0x01,0x65,0x4b,0x85,0xd8,0xa9,0xff,0x65,0x4c,0x85,0xd9,0xa0,0xff,0xc8,0xb9,0x00, +0x10,0x91,0x4b,0xc0,0xff,0xd0,0xf6,0x98,0x65,0x4b,0x85,0x4b,0x90,0x02,0xe6,0x4c, +0x06,0xee,0x4c,0x24,0x00,0x00,0xbc,0x00,0x00,0x84,0xee,0x26,0xee,0xe8,0xd0,0x06, +0xe6,0xf1,0xe6,0x43,0xe6,0xac,0x60,0xc6,0x01,0x58,0x85,0x98,0x4c +}; + +// Some definitions for compiler independence +#ifdef _MSC_VER +# include +# include +#else +# include +#endif + +#undef min +#undef max +#define remainder remainder_ + +// The main crunching structure +typedef struct { + signed short match_length; + unsigned short match_offset; + + union { + signed hash_link; + unsigned cumulative_cost; + }; +} lz_info; + +typedef struct { + unsigned char *src_data; + unsigned src_begin; + unsigned src_end; + unsigned output_begin; + unsigned output_end; + bool full_dict; + signed margin; + + FILE *dst_file; + unsigned dst_bits; + unsigned dst_used; + + unsigned file_size; + + lz_info *info; + + signed hash_table[0x100]; + //be graceful, only two max literals is a bit low, as tere can be uncompressibel data rows even bigger than that + unsigned char dst_literals[65536]; + unsigned output_type; + unsigned end_pos; + unsigned header_size; + signed load_addr; + signed depack_to; + unsigned cut_addr_first; + unsigned cut_addr_last; + signed start_addr; + signed relocate_to; + + const char *input_name; + char *output_name; + const char *emit_offset_tables; + unsigned iterations; + + bool offset_lengths; + bool write_tables; + bool show_stats; + bool is_cbm; + bool show_trace; + bool overlap; + bool output; + bool checksum; + bool exit_on_warn; + + // Some informational counters + struct { + unsigned output_size; + unsigned short_freq[4]; + unsigned long_freq[4]; + unsigned literal_bytes; + unsigned literal_runs; + unsigned match_bytes; + unsigned match_count; + unsigned offset_distance; + unsigned max_offset; + unsigned len_freq[259]; + unsigned llen_freq[259]; + } stats; +} lz_context; + +// A bit of global configuration data +typedef struct { + unsigned bits; + unsigned base; + signed limit; +} offset_length_t; + +static offset_length_t cfg_short_offset[4]; +static offset_length_t cfg_long_offset[4]; +#define cfg_short_limit (cfg_short_offset[3].limit) +#define cfg_long_limit (cfg_long_offset[3].limit) + +/****************************************************************************** + * Various utility functions and bithacks + ******************************************************************************/ +#define countof(n) (sizeof(n) / sizeof *(n)) + +unsigned _log2(unsigned value) { +# ifdef __GNUC__ + enum { WORD_BITS = sizeof(unsigned) * CHAR_BIT }; + + return (WORD_BITS - 1) ^ __builtin_clz(value); +# else + signed bits = -1; + + do + ++bits; + while(value >>= 1); + + return bits; +# endif +} + +bool wraps(unsigned cursor, unsigned length, unsigned limit) { + return ((cursor + length) ^ cursor) >= limit; +} + +unsigned remainder(signed cursor, signed window) { + return -(cursor | -window); +} + +unsigned min(unsigned a, unsigned b) { + return (a < b) ? a : b; +} + +unsigned max(unsigned a, unsigned b) { + return (a < b) ? b : a; +} + +#ifdef _MSC_VER +__declspec(noreturn) +#elif defined(__GNUC__) +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +#endif +static void +#ifdef _MSC_VER +__cdecl +#endif + +fatal(const char *format, ...) { + va_list args; + + va_start(args, format); + fputs("error: ", stderr); + vfprintf(stderr, format, args); + fputc('\n', stderr); + va_end(args); + + exit(EXIT_FAILURE); +} + +/****************************************************************************** + * Manage the output stream + ******************************************************************************/ +void _output_doflush(lz_context *ctx) { + putc(ctx->dst_bits, ctx->dst_file); + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +void output_open(lz_context *ctx, const char *name) { + if(ctx->dst_file = fopen(name, "wb"), !ctx->dst_file) + fatal("cannot create '%s'", name); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +void output_close(lz_context *ctx) { + if(ctx->dst_bits != 1) { + while(ctx->dst_bits < 0x100) + ctx->dst_bits <<= 1; + + putc(ctx->dst_bits, ctx->dst_file); + } + + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + ctx->stats.output_size = ftell(ctx->dst_file); + fclose(ctx->dst_file); +} + +void output_bit(lz_context *ctx, unsigned bit) { + ctx->file_size++; + if (ctx->output) { + if(ctx->dst_bits >= 0x100) + _output_doflush(ctx); + + if (ctx->show_trace) printf("%d", bit & 1); + ctx->dst_bits <<= 1; + ctx->dst_bits += bit & 1; + } +} + +void output_literal(lz_context *ctx, unsigned value) { + ctx->file_size += 8; + if (ctx->output) { + ctx->dst_literals[ctx->dst_used++] = value; + } +} + +unsigned output_bitsize(lz_context *ctx) { + unsigned total; + unsigned bitbuffer; + + total = ftell(ctx->dst_file); + total += ctx->dst_used; + total <<= 3; + + for(bitbuffer = ctx->dst_bits; bitbuffer > 1; bitbuffer >>= 1) + ++total; + + return total; +} + + +/****************************************************************************** + * Read file into memory and allocate per-byte buffers + ******************************************************************************/ +void read_input(lz_context *ctx, const char *name, bool is_cbm) { + FILE *file; + signed length; + unsigned origin; + + if(file = fopen(name, "rb"), !file) + fatal("unable to open '%s'", name); + +# ifdef _MSC_VER + length = _filelength(_fileno(file)); +# else + { + struct stat stat; + stat.st_size = 0; + fstat(fileno(file), &stat); + length = stat.st_size; + } +# endif + + if(length <= 0) + fatal("cannot determine length of '%s'", name); + + { + // Give us a sentinel for the info structure and prevent two-byte + // hashing from overrunning the buffer + unsigned count = length + 1; + + ctx->info = malloc(count * + (sizeof *ctx->info + sizeof *ctx->src_data)); + ctx->src_data = (void *) &ctx->info[count]; + + if(!ctx->info) + fatal("cannot allocate memory buffer"); + + if(fread(ctx->src_data, length, 1, file) != 1) + fatal("cannot read '%s'", name); + } + + // Deal with the PRG file header. We don't write the loading + // address back out to compressed file, however we *do* need to + // consider the decompression address when deciding whether a + // run crosses a page or window boundary + origin = 0; + + if(is_cbm) { + length -= 2; + + if(length < 0) { + fatal("CBM .prg file is too short to " + "fit a 2-byte load address"); + } + + origin = *ctx->src_data++; + origin += *ctx->src_data++ << 8; + } + + ctx->info -= origin; + ctx->src_data -= origin; + ctx->src_begin = origin; + ctx->src_end = origin + length; +} + +void cut_input_addr(lz_context *ctx, unsigned first, unsigned last, bool full_dict) { + ctx->output_begin = max(ctx->src_begin, first); + ctx->output_end = min(ctx->src_end, last); + //if dict shall be restricted to current range only, src end/begin is set to output end/begin + if (!full_dict) { + ctx->src_begin = ctx->output_begin; + ctx->src_end = ctx->output_end; + } + if(ctx->src_begin > ctx->src_end) + fatal("no data in address range $%04x $%04x", ctx->src_begin, ctx->src_end); +} + +/****************************************************************************** + * Try to figure out what matches would be the most beneficial + ******************************************************************************/ + +unsigned costof_run_8class(unsigned run) { + return _log2(run) * 2 + 1; +} + +unsigned costof_run(unsigned run) { + unsigned bits; + run--; + if (run == 0) return 1; + if (run == 1) return 2; + bits = _log2(run); + return bits * 2 + 2; + //return _log2(run) * 2 + 1; +} + +unsigned costof_literals_8class(unsigned address, unsigned length) { + unsigned cost; + + cost = length * 8; + cost += costof_run_8class(length); + + // A type bit is still always needed after maximum length + // run since another run may follow + if(length == RUN_LIMIT) cost++; + return cost; +} + +unsigned costof_literals(unsigned address, unsigned length) { + unsigned cost; + + cost = length * 8; + cost += _log2(length) * 2 + 1; //costof_run(length); + + // A type bit is still always needed after maximum length + // run since another run may follow + if(length == RUN_LIMIT) cost++; + return cost; +} + +unsigned costof_match_8class(const offset_length_t *class, signed offset, unsigned length) { + unsigned cost = 3; + + while(offset > class->limit) + ++class; + cost += class->bits; + + return cost + costof_run_8class(length - 1); +} + +unsigned costof_match(const offset_length_t *class, signed offset, unsigned length) { + unsigned cost = 3; + + while(offset > class->limit) + ++class; + cost += class->bits; + + return cost + costof_run(length - 1); +} + +lz_info optimal_parsing_literal(lz_context *ctx, const lz_info *info, unsigned cursor) { + signed length; + unsigned cost; + lz_info result; + + length = -info[cursor + 1].match_length; + + if(length > 0 && length < RUN_LIMIT) + cost = info[cursor + ++length].cumulative_cost; + else { + cost = info[cursor + 1].cumulative_cost; + length = 1; + } + + if (ctx->output_type == OUTPUT_BITFIRE) { + cost += costof_literals_8class(cursor, length); + } else { + cost += costof_literals(cursor, length); + } + + result.match_length = -length; + result.cumulative_cost = cost; + + return result; +} + +lz_info optimal_parsing (lz_context *ctx, const lz_info *info, unsigned cursor, signed match_offset, unsigned match_length, unsigned match_limit, lz_info best_match) { + unsigned cost; + + if(match_length == 2) { + if(match_offset <= cfg_short_limit) { + if (ctx->output_type == OUTPUT_BITFIRE) { + cost = costof_match_8class(cfg_short_offset, match_offset, match_length); + } else { + cost = costof_match(cfg_short_offset, match_offset, match_length); + } + goto try_short_match; + } else if(++match_length > match_limit) + return best_match; + } + + do { + if (ctx->output_type == OUTPUT_BITFIRE) { + cost = costof_match_8class(cfg_long_offset, match_offset, match_length); + } else { + cost = costof_match(cfg_long_offset, match_offset, match_length); + } +try_short_match: + cost += info[cursor + match_length].cumulative_cost; + + if(cost < best_match.cumulative_cost) { + best_match.match_offset = match_offset; + best_match.match_length = match_length; + best_match.cumulative_cost = cost; + } + } while(++match_length <= match_limit); + + return best_match; +} + +/****************************************************************************** + * Determine the longest match for every position of the file + ******************************************************************************/ +signed *hashof(lz_context *ctx, unsigned a, unsigned b) { + static const unsigned char random[] = { + 0x17, 0x80, 0x95, 0x4f, 0xc7, 0xd1, 0x15, 0x13, + 0x91, 0x57, 0x0f, 0x47, 0xd0, 0x59, 0xab, 0xf0, + 0xa7, 0xf5, 0x36, 0xc0, 0x24, 0x9c, 0xed, 0xfd, + 0xd4, 0xf3, 0x51, 0xb4, 0x8c, 0x97, 0xa3, 0x58, + 0xcb, 0x61, 0x78, 0xb1, 0x3e, 0x7e, 0xfb, 0x41, + 0x39, 0xa6, 0x8e, 0x10, 0xa1, 0xba, 0x62, 0xcd, + 0x94, 0x02, 0x0d, 0x2b, 0xdb, 0xd7, 0x44, 0x16, + 0x29, 0x4d, 0x68, 0x0a, 0x6b, 0x6c, 0xa2, 0xf8, + 0xc8, 0x9f, 0x25, 0xca, 0xbd, 0x4a, 0xc2, 0x35, + 0x53, 0x1c, 0x40, 0x04, 0x76, 0x43, 0xa9, 0xbc, + 0x46, 0xeb, 0x99, 0xe9, 0xf6, 0x5e, 0x8f, 0x8a, + 0xf1, 0x5d, 0x21, 0x33, 0x0b, 0x82, 0xdf, 0x52, + 0xea, 0x27, 0x22, 0x9a, 0x6f, 0xad, 0xe5, 0x83, + 0x11, 0xbe, 0xa4, 0x85, 0x1d, 0xb3, 0x77, 0xf4, + 0xef, 0xb7, 0xf2, 0x03, 0x64, 0x6d, 0x1b, 0xee, + 0x72, 0x08, 0x66, 0xc6, 0xc1, 0x06, 0x56, 0x81, + 0x55, 0x60, 0x70, 0x8d, 0x23, 0xb2, 0x65, 0x5b, + 0xff, 0x4c, 0xb9, 0x7a, 0xd6, 0xe6, 0x19, 0x9b, + 0xb5, 0x49, 0x7d, 0xd8, 0x45, 0x1a, 0x84, 0x32, + 0xdd, 0xbf, 0x9e, 0x2f, 0xd2, 0xec, 0x92, 0x0e, + 0xe8, 0x7c, 0x7f, 0x00, 0x86, 0xde, 0xb6, 0xcf, + 0x05, 0x69, 0xd5, 0x37, 0xe4, 0x30, 0x3c, 0xe1, + 0x4b, 0xaa, 0x3b, 0x2d, 0xda, 0x5c, 0xcc, 0x67, + 0x20, 0xb0, 0x6a, 0x1f, 0xf9, 0x01, 0xac, 0x2e, + 0x71, 0xf7, 0xfc, 0x3f, 0x42, 0xd3, 0xbb, 0xa8, + 0x38, 0xce, 0x12, 0x96, 0xe2, 0x14, 0x87, 0x4e, + 0x63, 0x07, 0xae, 0xdc, 0xa5, 0xc9, 0x0c, 0x90, + 0xe7, 0xd9, 0x09, 0x2a, 0xc4, 0x3d, 0x5a, 0x34, + 0x8b, 0x88, 0x98, 0x48, 0xfa, 0xc3, 0x26, 0x75, + 0xfe, 0xa0, 0x7b, 0x50, 0x2c, 0x89, 0x18, 0x9d, + 0x3a, 0x73, 0x6e, 0x5f, 0xc5, 0xaf, 0xb8, 0x74, + 0x93, 0xe3, 0x79, 0x28, 0xe0, 0x1e, 0x54, 0x31 + }; + + size_t bucket = random[a] ^ b; + return &ctx->hash_table[bucket]; +} + +void generate_hash_table(lz_context *ctx) { + unsigned cursor; + + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + for(cursor = 0; cursor < countof(ctx->hash_table); ++cursor) + ctx->hash_table[cursor] = INT_MIN; + + for(cursor = ctx->src_begin; cursor != src_end; ++cursor) { + signed *hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + info[cursor].hash_link = *hash_bucket; + *hash_bucket = cursor; + } +} + +unsigned find_matches(lz_context *ctx) { + const unsigned src_begin = ctx->src_begin; + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + unsigned offset_limit = min(INFINITE_WINDOW, cfg_long_limit); + unsigned cursor = ctx->src_end; + + info[cursor].cumulative_cost = 0; + + while(cursor != src_begin) { + unsigned match_length; + signed cursor_limit; + unsigned length_limit; + signed *hash_bucket; + signed hash_link; + lz_info best_match; + + --cursor; + + match_length = 1; + cursor_limit = cursor - offset_limit; + + length_limit = RUN_LIMIT; + length_limit = min(length_limit, remainder(cursor, INFINITE_WINDOW)); + length_limit = min(length_limit, src_end - cursor); + + hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + assert((unsigned) *hash_bucket == cursor); + hash_link = info[cursor].hash_link; + *hash_bucket = hash_link; + + best_match = optimal_parsing_literal(ctx, info, cursor); + + while(hash_link >= cursor_limit) { + unsigned match_limit = remainder(hash_link, INFINITE_WINDOW); + match_limit = min(match_limit, length_limit); + + if(match_length != match_limit) { + unsigned i = match_length + 1; + + if(!memcmp(&src_data[cursor], &src_data[hash_link], i)) { + for(; i != match_limit; ++i) { + if(src_data[cursor + i] != src_data[hash_link + i]) + break; + } + + assert(i <= match_limit); + + best_match = optimal_parsing ( + ctx, + info, + cursor, + cursor - hash_link, + match_length + 1, + i, + best_match + ); + + match_length = i; + + if(match_length == RUN_LIMIT) + break; + } + } + + hash_link = info[hash_link].hash_link; + } + + info[cursor] = best_match; + } + return info[src_begin].cumulative_cost; +} + + +/****************************************************************************** + * Write the generated matches and literal runs + ******************************************************************************/ + +// burps out remaining literals as a plain bnary blob +void encode_literals_plain (lz_context *ctx, unsigned cursor, unsigned length) { + const unsigned char *data; + unsigned start = length; + + if(ctx->show_trace) { + printf ("plain literal(%u bytes)\n",length); + } + + ctx->stats.literal_bytes += length; + ++ctx->stats.literal_runs; + ++ctx->stats.llen_freq[length]; + + data = &ctx->src_data[cursor]; + do + output_literal(ctx, data[start - length--]); + while(length); +} + +void encode_literals_8class (lz_context *ctx, unsigned cursor, unsigned length) { + signed bit; + const unsigned char *data; + unsigned start = length; + + if(ctx->show_trace) { + printf ("literal(%u bytes)\n",length); + } + ctx->stats.literal_bytes += length; + ++ctx->stats.literal_runs; + ++ctx->stats.llen_freq[length]; + + bit = _log2(length); + while(--bit >= 0) { + output_bit(ctx, 1); + output_bit(ctx, length >> bit); + } + + output_bit(ctx, 0); + + data = &ctx->src_data[cursor]; + do + output_literal(ctx, data[start - length--]); + while(length); +} + +void encode_literals (lz_context *ctx, unsigned cursor, unsigned length) { + signed bit; + const unsigned char *data; + unsigned start = length; + + ctx->stats.literal_bytes += length; + ++ctx->stats.literal_runs; + ++ctx->stats.llen_freq[length]; + + if(ctx->show_trace) printf ("length: "); + bit = _log2(length); + while(--bit >= 0) { + output_bit(ctx, 1); + output_bit(ctx, length >> bit); + if (ctx->show_trace) printf(" "); + } + + output_bit(ctx, 0); + + data = &ctx->src_data[cursor]; + do + output_literal(ctx, data[start - length--]); + while(length); + if (ctx->show_trace) printf("\n"); +} + +void encode_match_8class (lz_context *ctx, signed offset, unsigned length) { + unsigned offset_bits; + unsigned offset_prefix; + const offset_length_t *offset_class; + signed length_bit; +#if BITFIRE_WITH_MOFF + const unsigned prefix_order[] = {0,1,2,3,7,6,5,4}; +#else + const unsigned prefix_order[] = {1,2,0,3,4,5,6,7}; +#endif + + if(ctx->show_trace) { + printf("match(-%u, %u bytes)\n",offset,length); + } + + ++ctx->stats.match_count; + ctx->stats.match_bytes += length; + ctx->stats.offset_distance += offset; + + // Write length + length_bit = _log2(--length) - 1; + + // Write offset prefix + if(length == 2 - 1) { + offset_prefix = 0; + assert(offset <= cfg_short_limit); + offset_class = cfg_short_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + offset_prefix = prefix_order[offset_prefix]; + + ++ctx->stats.short_freq[offset_prefix & 3]; + } else { + offset_prefix = 4; + assert(offset <= cfg_long_limit); + offset_class = cfg_long_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + offset_prefix = prefix_order[offset_prefix]; + + ++ctx->stats.long_freq[offset_prefix & 3]; + } + + output_bit(ctx, offset_prefix >> 2); + + while(length_bit >= 0) { + output_bit(ctx, length >> length_bit); + output_bit(ctx, --length_bit < 0); + } + + output_bit(ctx, offset_prefix >> 1); + output_bit(ctx, offset_prefix >> 0); + + // Write offset payload + offset -= offset_class->base; + + offset_bits = offset_class->bits; + if (offset_bits > 7) { + while(offset_bits & 7) output_bit(ctx, offset >> --offset_bits); + } else { + while(offset_bits & 7) output_bit(ctx, ~offset >> --offset_bits); + } + if(offset_bits) output_literal(ctx, ~offset); + ++ctx->stats.len_freq[length + 2]; +} + +void encode_match (lz_context *ctx, signed offset, unsigned length) { + unsigned offset_bits; + unsigned offset_prefix; + const offset_length_t *offset_class; + signed length_bit; + unsigned init_bit; + + if (offset == 0) length = RUN_LIMIT + 2; + ++ctx->stats.match_count; + ctx->stats.match_bytes += length; + ctx->stats.offset_distance += offset; + if (ctx->stats.max_offset < offset) ctx->stats.max_offset = offset; + + if(ctx->show_trace) printf ("length: "); + // Write initial length bit + length_bit = _log2(--length); + init_bit = --length_bit < 0; + output_bit(ctx, init_bit); + + length--; + + //nothing to do if length == 1, as the only needed bit is already written + if (length == 0) { + } else if (length == 1) { + //special case, no additional bits will follow after 0 as payload is 0 + output_bit(ctx, 0); + //now handle lengths > 2 + } else { + output_bit(ctx, 1); + length_bit = _log2(length); + while(--length_bit >= 0) { + if (ctx->show_trace) printf(" "); + output_bit(ctx, length >> length_bit); + //if (ctx->show_trace) printf(" "); + if (length >= 255) { + //skip stop bit if number has 8 bits + if (length_bit != 0) { + output_bit(ctx, length_bit != 0); + } else { + if (ctx->show_trace) printf("s"); + } + } else { + output_bit(ctx, length_bit != 0); + } + } + } + if (ctx->show_trace) printf("\n"); + + if (length >= RUN_LIMIT) return; + + // Write offset prefix + if(length == 0) { + assert(offset <= cfg_short_limit); + offset_prefix = 0; + offset_class = cfg_short_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.short_freq[offset_prefix]; + ++ctx->stats.len_freq[length + 2]; + + // Note that the encoding for short matches is reversed relative to + // the long ones in order to expose holes in the decruncher's offset + // tables + offset_prefix = ~offset_prefix; + } else { + assert(offset <= cfg_long_limit); + offset_prefix = 0; + offset_class = cfg_long_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.long_freq[offset_prefix]; + ++ctx->stats.len_freq[length + 2]; + } + + if(ctx->show_trace) printf("offset: (%d)", init_bit); + output_bit(ctx, offset_prefix >> 1); + output_bit(ctx, offset_prefix >> 0); + if(ctx->show_trace) printf("-"); + + // Write offset payload + offset -= offset_class->base; + offset = ~offset; + + offset_bits = offset_class->bits; + while(offset_bits & 7) + output_bit(ctx, offset >> --offset_bits); + + if(offset_bits) { + output_literal(ctx, offset); + if (ctx->show_trace) printf("$%02x", offset & 0xff); + } + + if (ctx->show_trace) printf("\n"); +} + +void render_output(lz_context *ctx) { + unsigned cursor; + + bool implicit_match = false; + +// unsigned output_end = ctx->output_end; + signed dest_pos; + signed stream_pos; + + lz_info *info = ctx->info; + signed length; + signed length_bit; + signed packed_size; + signed last_match; + + bool update = true; + + bool sentinel_needed = false; + + ctx->margin = 0; + ctx->end_pos = 0; + + packed_size = ctx->file_size; + last_match = 0; + + ctx->file_size = 0; + length = 0; + + for(cursor = ctx->output_begin; cursor < ctx->output_end; cursor += length) { + length = info[cursor].match_length; + + //check for normal overlap + if (ctx->output) { + //save pointer before new match/literal is written onto stream + stream_pos = (packed_size + 7) / 8 - (ctx->file_size + 7) / 8; + //distance to end from current destination + dest_pos = ctx->output_end - cursor; + if(stream_pos > dest_pos && stream_pos - dest_pos > ctx->margin) { + ctx->margin = stream_pos - dest_pos; + } + //only allow unencoded last literal if zero overlap and if load_addr && depack_to is yet unset (--load-addr/--dpeack-to not set) + if(ctx->load_addr < 0 && ctx->depack_to < 0 && !ctx->overlap && stream_pos > dest_pos) { + if(update) { + //last sane position, now switch to literal output only + ctx->end_pos = cursor; + update = false; + } + } + } + + if(length > 0) { + + unsigned offset = info[cursor].match_offset; + sentinel_needed = false; + + if(!implicit_match) { + if(ctx->show_trace) printf ("type_bit "); + if(update) output_bit(ctx, 0); + if(ctx->show_trace) printf ("\n"); + } + + if (ctx->output_type == OUTPUT_BITFIRE) { + if (!update) { + encode_literals_plain(ctx, cursor, length); + } else { + encode_match_8class(ctx, offset, length); + } + } else { + encode_match(ctx, offset, length); + } + if(ctx->show_trace) printf ("\n"); + + implicit_match = false; + last_match = cursor + length; + } else { + sentinel_needed = true; + length = -length; + + if(ctx->show_trace) printf ("type_bit "); + if(update) output_bit(ctx, 1); + if(ctx->show_trace) printf ("\n"); + + // Normally a match implicitly follows a literal run except for the + // case of a maximum length literal run + implicit_match = length < RUN_LIMIT; + + // The parser may generate a short run followed by one or more maximum + // length runs for split literals. This needs to be avoided manually + // by reversing the order + if(implicit_match) { + signed next_length = -info[cursor + length].match_length; + //is the next run a literal too? is the length of the current literal smaller then the run limit and is the next element a literal of maximum runlength? If so, swap both, and if things continue, do so also on upcoming turns. + if(next_length > 0 && next_length == RUN_LIMIT && length < RUN_LIMIT) { + //if(next_length > 0) { + info[cursor].match_length = -next_length; + info[cursor + next_length].match_length = -length; + + //check that first element is < RUN_LIMIT if we swap elements + assert(length < RUN_LIMIT); + + length = next_length; + implicit_match = false; + } + } + + if (ctx->output_type == OUTPUT_BITFIRE) { + if (!update) { + encode_literals_plain(ctx, cursor, length); + } else { + encode_literals_8class(ctx, cursor, length); + } + } + else { + encode_literals(ctx, cursor, length); + } + if(ctx->show_trace) printf ("\n"); + } + } + + // If still no end_position is determined, do it now + if (update) ctx->end_pos = last_match; + +# if VERIFY_COST_MODEL + { + unsigned expected = info[ctx->output_begin].cumulative_cost; + unsigned actual = ctx->file_size; + + if(expected != actual) { + printf ( + "expected: %u\n" + "actual: %u\n", + expected, + actual + ); + } + } +# endif + + //check for normal overlap + if (ctx->output) { + //save pointer before new match/literal is written onto stream + stream_pos = (packed_size + 7) / 8 - (ctx->file_size + 7) / 8; + //distance to end from current destination + dest_pos = ctx->output_end - cursor; + if(stream_pos > dest_pos && stream_pos - dest_pos > ctx->margin) { + ctx->margin = stream_pos - dest_pos; + } + } + + // The sentinel is a maximum-length match + if(ctx->show_trace) printf("EOF\n"); + + // We encode the EOF whenever we do compress with overlap or to an alternative location where in place depacking can't happen + if(ctx->overlap || ctx->load_addr >= 0 || ctx->depack_to >= 0) { + // In that case take end of data as end_pos to disable the end_pos check by letting it hit too late during decoding + // XXX TODO can still be omitted if last action was a match + ctx->end_pos = ctx->output_end; + + // only add a sentinael if last action was a literal + if (sentinel_needed) { + if(!implicit_match) { + if(ctx->show_trace) printf ("type_bit "); + output_bit(ctx, 0); + if(ctx->show_trace) printf ("\n"); + } + +// printf("%d bits saved\n", saved); + + if (ctx->output_type == OUTPUT_BITFIRE) { + length_bit = _log2(RUN_LIMIT); + output_bit(ctx, --length_bit >= 0); + + while(length_bit >= 0) { + output_bit(ctx, RUN_LIMIT >> length_bit); + output_bit(ctx, --length_bit < 0); + } + } else { + encode_match(ctx, 1, RUN_LIMIT+3); + } + } + } + + return; +} + + +/****************************************************************************** + * Parse out the set of offset bit lengths from a descriptor string + ******************************************************************************/ + +//XXX TODO basically the same, except table->base = 0 and base is unused? +static void prepare_offset_lengths_8class(offset_length_t *table, size_t count) { + unsigned limit = 0; + unsigned previous = 0; + + do { + unsigned int bits = table->bits; + + if(bits <= previous) + fatal("offset lengths must be listed in ascending order"); + previous = bits; + if(bits > OFFSET_LENGTH_LIMIT) + fatal("offset lengths cannot be wider than %u bits", OFFSET_LENGTH_LIMIT); + + limit = 1 << bits; + table->base = 1; + table->limit = limit; + ++table; + } while(--count); +} + +static void prepare_offset_lengths(offset_length_t *table, size_t count) { + unsigned base; + unsigned limit = 0; + unsigned previous = 0; + + do { + unsigned int bits = table->bits; + + if(bits <= previous) + fatal("offset lengths must be listed in ascending order"); + previous = bits; + if(bits > OFFSET_LENGTH_LIMIT) + fatal("offset lengths cannot be wider than %u bits", OFFSET_LENGTH_LIMIT); + + base = limit + 1; + limit += 1 << bits; + table->base = base; + table->limit = limit; + ++table; + } while(--count); +} + +bool parse_offset_lengths(lz_context* ctx, const char *text) { + if(sscanf(text, "%u/%u/%u/%u:%u/%u/%u/%u", + &cfg_short_offset[0].bits, &cfg_short_offset[1].bits, + &cfg_short_offset[2].bits, &cfg_short_offset[3].bits, + &cfg_long_offset[0].bits, &cfg_long_offset[1].bits, + &cfg_long_offset[2].bits, &cfg_long_offset[3].bits) != 8) { + return false; + } + if (ctx->output_type == OUTPUT_BITFIRE && !BITFIRE_WITH_MOFF) { + prepare_offset_lengths_8class(cfg_short_offset, 4); + prepare_offset_lengths_8class(cfg_long_offset, 4); + } else { + prepare_offset_lengths(cfg_short_offset, 4); + prepare_offset_lengths(cfg_long_offset, 4); + } + return true; +} + + +/****************************************************************************** + * Generate decruncher the tables corresponding to a particular offset length + * sequence. + * These are admittedly rather convoluted and tied tightly to how matches are + * handled in the implementation. See the source for details + ******************************************************************************/ + +static char single_offset (const offset_length_t *class, unsigned shift) { + signed offset = class->base + 1; + if(class->bits > 8) + offset += 0x8000; + else if(class->bits == 8) + offset += 0x0100; + offset = -offset; + offset >>= shift; + offset &= 0xFF; + + return offset; +} + +static void write_single_offset (FILE *file, const offset_length_t *class, unsigned shift) { + char binary[9]; + + signed offset = class->base + 1; + if(class->bits > 8) + offset += 0x8000; + else if(class->bits == 8) + offset += 0x0100; + offset = -offset; + offset >>= shift; + offset &= 0xFF; + + { + char *digit = &binary[8]; + *digit = '\0'; + do { + *--digit = (offset & 1)["01"]; + offset >>= 1; + } while(digit != binary); + } + + fprintf ( + file, + "%s\t\t.byte %%%s\t\t;%u-%u%s\n", + class->bits < shift ? ";" : "", + binary, + class->base, + class->limit, + class->bits < shift ? " (unreferenced)" : "" + ); +} + +void write_offsets(FILE* file) { + static const char length_codes[] = { + 0, + 0xff, + 0x7f, + 0x3f, + 0x1f, + 0x0f, + 0x07, + 0x03, + 0, + 0xbf, + 0x5f, + 0x2f, + 0x17, + 0x0b, + 0x05, + 0x02 + }; + + ptrdiff_t i; + + for(i = 0; i <= 3; ++i) { + unsigned bits = cfg_long_offset[i].bits; + fputc(length_codes[bits], file); + } + for(i = 3; i >= 0; --i) { + unsigned bits = cfg_short_offset[i].bits; + fputc(length_codes[bits], file); + } + for(i = 0; i <= 3; ++i) + fputc(single_offset(&cfg_long_offset[i], 0), file); + for(i = 3; i >= 0; --i) + fputc(single_offset(&cfg_short_offset[i], 0), file); + // MSB of base offsets + for(i = 0; i <= 3; ++i) + fputc(single_offset(&cfg_long_offset[i], 8), file); + for(i = 3; i >= 0; --i) + fputc(single_offset(&cfg_short_offset[i], 8), file); +} + +void write_offset_tables(const char *name) { + static const char long_title[] = "\t\t;Long (>2 byte matches)\n"; + static const char short_title[] = "\t\t;Short (2 byte matches)\n"; + + static const char *const length_codes[] = { + NULL, + "11111111", // 1-bits + "01111111", // 2-bits + "00111111", // 3-bits + "00011111", // 4-bits + "00001111", // 5-bits + "00000111", // 6-bits + "00000011", // 7-bits + "00000000", // 8-bits: This needs a bit of special-processing + "10111111", // 9-bits + "01011111", // 10-bits + "00101111", // 11-bits + "00010111", // 12-bits + "00001011", // 13-bits + "00000101", // 14-bits + "00000010" // 15-bits + }; + + ptrdiff_t i; + unsigned near_longs; + + // Open the target file + FILE *file = fopen(name, "w"); + if(!file) + fatal("cannot create '%s'", name); + + // Bit lengths + fprintf(file, "_lz_moff_length\n"); + fprintf(file, long_title); + near_longs = 0; + for(i = 0; i <= 3; ++i) { + unsigned bits = cfg_long_offset[i].bits; + fprintf(file, "\t\t.byte %%%s\t\t;%u bits\n", + length_codes[bits], bits); + if(bits < 8) + ++near_longs; + } + fprintf(file, short_title); + for(i = 3; i >= 0; --i) { + unsigned bits = cfg_short_offset[i].bits; + fprintf(file, "\t\t.byte %%%s\t\t;%u bits\n", + length_codes[bits], bits); + } + // LSB of base offsets + fprintf(file, "_lz_moff_adjust_lo\n"); + fprintf(file, long_title); + for(i = 0; i <= 3; ++i) + write_single_offset(file, &cfg_long_offset[i], 0); + fprintf(file, short_title); + for(i = 3; i >= 0; --i) + write_single_offset(file, &cfg_short_offset[i], 0); + // MSB of base offsets + fprintf(file, "_lz_moff_adjust_hi = *-%u\n", near_longs); + fprintf(file, long_title); + for(i = 0; i <= 3; ++i) + write_single_offset(file, &cfg_long_offset[i], 8); + fprintf(file, short_title); + for(i = 3; i >= 0; --i) + write_single_offset(file, &cfg_short_offset[i], 8); + + fclose(file); +} + + +/****************************************************************************** + * Print some basic statistics about the encoding of the file + ******************************************************************************/ +void print_statistics(const lz_context *ctx, FILE *file) { + unsigned input_size = ctx->src_end - ctx->src_begin; + unsigned i; + + printf ( + "input file:\t" "%u bytes\n" + "output file:\t" "%u bytes, %u bits (%.2f%% ratio)\n" + "short offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "long offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "%u matches:\t" "%u bytes, %f avg, %d max\n" + "%u literals:\t" "%u bytes, %f avg\n" + "avg offset:\t" "%f bytes\n", + + input_size, + ctx->stats.output_size, + ctx->info[ctx->src_begin].cumulative_cost, + 100.0 * ctx->stats.output_size / input_size, + + cfg_short_offset[0].base, + cfg_short_offset[0].limit, + ctx->stats.short_freq[0], + cfg_short_offset[1].base, + cfg_short_offset[1].limit, + ctx->stats.short_freq[1], + cfg_short_offset[2].base, + cfg_short_offset[2].limit, + ctx->stats.short_freq[2], + cfg_short_offset[3].base, + cfg_short_offset[3].limit, + ctx->stats.short_freq[3], + cfg_long_offset[0].base, + cfg_long_offset[0].limit, + ctx->stats.long_freq[0], + cfg_long_offset[1].base, + cfg_long_offset[1].limit, + ctx->stats.long_freq[1], + cfg_long_offset[2].base, + cfg_long_offset[2].limit, + ctx->stats.long_freq[2], + cfg_long_offset[3].base, + cfg_long_offset[3].limit, + ctx->stats.long_freq[3], + + ctx->stats.match_count, + ctx->stats.match_bytes, + (double) ctx->stats.match_bytes / ctx->stats.match_count, + ctx->stats.max_offset, + + ctx->stats.literal_runs, + ctx->stats.literal_bytes, + (double) ctx->stats.literal_bytes / ctx->stats.literal_runs, + + (double) ctx->stats.offset_distance / ctx->stats.match_count + ); + for (i = 0; i < 256; i++) { + printf("% 6d % 6d\n", ctx->stats.llen_freq[i], ctx->stats.len_freq[i]); + } +} + +/****************************************************************************** + * Helper functions + ******************************************************************************/ +signed read_number(char* arg) { + if(arg[0] == '$') return strtoul(arg + 1, NULL, 16); + else if(arg[0] == '0' && arg[1] == 'x') return strtoul(arg + 2, NULL, 16); + return strtoul(arg, NULL, 10); +} + +unsigned compress(lz_context* ctx, char* output_name) { + generate_hash_table(ctx); + return find_matches(ctx); +} + +/****************************************************************************** + * + ******************************************************************************/ + +void iterate(lz_context* ctx) { + unsigned packed_size; + + unsigned temp; + signed j; + unsigned smin, smax; + unsigned lmin, lmax; + unsigned from, to; + + char lengths[24]; + unsigned lbits[6], sbits[6]; + unsigned lbest, sbest; + + lbits[0] = 3; + lbits[1] = 7; + lbits[2] = 10; + lbits[3] = 13; + + setbuf(stdout, NULL); + + packed_size = 0; + smin = 1; + smax = 15; + + lmin = 1; + lmax = 15; + + ctx->iterations = 1; + while (ctx->iterations--) { + for (j = smin; j <= smax - 3; j++) { + sbits[0] = j; + sbits[3] = smax; + sbits[1] = j + (smax - j) / 3; + sbits[2] = j + (smax - j) / 3 * 2; + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + smin = j; packed_size = temp; + } + } + for (j = smax; j > smin + 3; j--) { + sbits[0] = smin; + sbits[3] = j; + sbits[1] = smin + (j - smin) / 3; + sbits[2] = smin + (j - smin) / 3 * 2; + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + smax = j; packed_size = temp; + } + + } + sbits[0] = smin; + sbits[3] = smax; + sbits[1] = smin + (smax - smin) / 3; + sbits[2] = smin + (smax - smin) / 3 * 2; + + for (j = lmin; j <= lmax - 3; j++) { + lbits[0] = j; + lbits[3] = lmax; + lbits[1] = j + (lmax - j) / 3; + lbits[2] = j + (lmax - j) / 3 * 2; + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + lmin = j; packed_size = temp; + } + } + for (j = lmax; j > lmin + 3; j--) { + lbits[0] = lmin; + lbits[3] = j; + lbits[1] = lmin + (j - lmin) / 3; + lbits[2] = lmin + (j - lmin) / 3 * 2; + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + lmax = j; packed_size = temp; + } + + } + lbits[0] = lmin; + lbits[3] = lmax; + lbits[1] = lmin + (lmax - lmin) / 3; + lbits[2] = lmin + (lmax - lmin) / 3 * 2; + + + } + + packed_size = 0; + ctx->iterations = 2; + + while (ctx->iterations--) { + + for (j = 0; j < 4; j++) { + lbest = lbits[j]; + if (j == 0) from = 1; + else from = lbits[j-1] + 1; + + if (j == 3) to = 16; + else to = lbits[j+1] - 1; + + for (lbits[j] = from; lbits[j] < to; lbits[j]++) { + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + lbest = lbits[j]; packed_size = temp; + } + } + lbits[j] = lbest; + } + for (j = 0; j < 4; j++) { + sbest = sbits[j]; + if (j == 0) from = 1; + else from = sbits[j-1] + 1; + + if (j == 3) to = 16; + else to = sbits[j+1] - 1; + + for (sbits[j] = from; sbits[j] < to; sbits[j]++) { + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + temp = compress(ctx, ctx->output_name); + if (packed_size == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + sbest = sbits[j]; packed_size = temp; + } + } + sbits[j] = sbest; + } + } + + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[0],sbits[1],sbits[2],sbits[3],lbits[0],lbits[1],lbits[2],lbits[3]); + parse_offset_lengths(ctx, lengths); + printf("best bitlengths: %s \n", lengths); +} + +int crunch(lz_context* ctx) { + unsigned packed_size; + unsigned source_size; + unsigned decruncher_size = sizeof decruncher; + unsigned data_addr = 0; + int i; + + unsigned char checksum; + + read_input(ctx, ctx->input_name, ctx->is_cbm); + cut_input_addr(ctx, ctx->cut_addr_first, ctx->cut_addr_last, ctx->full_dict); + + source_size = ctx->src_end - ctx->src_begin; + + if (ctx->iterations > 0) iterate(ctx); + + // Do the compression + generate_hash_table(ctx); + find_matches(ctx); + + output_open(ctx, ctx->output_name); + + if(ctx->output_type == OUTPUT_LEVEL || ctx->output_type == OUTPUT_BITFIRE) { + // Add 2 blank bytes here first, as the address can not be calculated yet + fputc(0, ctx->dst_file); + fputc(0, ctx->dst_file); + + // Add depack address + fputc(0, ctx->dst_file); + fputc(0, ctx->dst_file); + + if (!ctx->overlap) { + // Add 2 blank bytes here first, as the end_addr can not be calculated yet + fputc(0, ctx->dst_file); + fputc(0, ctx->dst_file); + } + } + + if(ctx->output_type == OUTPUT_SFX) { + // Output decruncher with loadadress 0x801 + fwrite(decruncher, decruncher_size, 1, ctx->dst_file); + + // Add depack address + fputc(ctx->start_addr & 0xff, ctx->dst_file); + fputc(ctx->start_addr >> 8, ctx->dst_file); + } + + if(ctx->write_tables) write_offsets(ctx->dst_file); + + // Do a dry run with rendering output to find out packed_filesize + ctx->output = false; + render_output(ctx); + + // Now go live and find perfect safety margin, and write compressed data to file + ctx->output = true; + render_output(ctx); + + output_close(ctx); + + // Now open compressed file and manipulate all kind of header data + if(ctx->dst_file = fopen(ctx->output_name, "rb+"), !ctx->dst_file) fatal("cannot create '%s'", ctx->output_name); + + // Packed size including all the header bytes + fseek(ctx->dst_file, 0L, SEEK_END); + packed_size = ftell(ctx->dst_file); + + // Ignore margin in case of zero overlap + if (!ctx->overlap) ctx->margin = 0; + + // Adapt size for different output types + if(ctx->output_type == OUTPUT_LEVEL || ctx->output_type == OUTPUT_BITFIRE) { + // File contains a load address, subtract + packed_size -= 2; + } else if(ctx->output_type == OUTPUT_SFX) { + // Includes already the load address + packed_size -= decruncher_size; + } + + // Emit margin only when necessary + if (ctx->margin > 0 && ctx->load_addr < 0 && ctx->depack_to < 0 && ctx->output_type != OUTPUT_SFX) { + printf("overlap: %d bytes\n", ctx->margin); + } + + // Some stats and info + printf("source size: $%04x (%d)\n", source_size, source_size); + printf("packed size: $%04x (%d) %s ratio: %.1f%%\n", packed_size - ctx->write_tables * 24, packed_size - ctx->write_tables * 24, ctx->write_tables ? "(+24 byte tables)" : "", ((packed_size - ctx->write_tables * 24) * 100.0 / (ctx->output_end - ctx->output_begin))); + + // Print more info and calc addresses + if(ctx->output_type == OUTPUT_LEVEL || ctx->output_type == OUTPUT_BITFIRE) { + //find perfect loading address + if (ctx->load_addr < 0) ctx->load_addr = ctx->output_end - packed_size + ctx->margin; + if (ctx->relocate_to >= 0 && ctx->load_addr >= 0) { + ctx->load_addr = ctx->load_addr + (ctx->relocate_to - ctx->output_begin); + ctx->end_pos = ctx->end_pos + (ctx->relocate_to - ctx->output_begin); + } + + printf("source load: $%04x-$%04x\n", ctx->output_begin, ctx->output_end); + printf("packed load: $%04x-$%04x\n", ctx->load_addr, ctx->load_addr + packed_size); + + // Fix optimal load address to file + fseek(ctx->dst_file, 0, SEEK_SET); + fputc(ctx->load_addr & 0xff, ctx->dst_file); + fputc(ctx->load_addr >> 8, ctx->dst_file); + // Add depack address + if (ctx->depack_to >=0) { + fputc(ctx->depack_to & 0xff, ctx->dst_file); + fputc(ctx->depack_to >> 8, ctx->dst_file); + } else { + if (ctx->relocate_to >= 0) { + fputc(ctx->relocate_to & 0xff, ctx->dst_file); + fputc(ctx->relocate_to >> 8, ctx->dst_file); + } else { + fputc(ctx->output_begin & 0xff, ctx->dst_file); + fputc(ctx->output_begin >> 8, ctx->dst_file); + } + } + if (!ctx->overlap) { + // Add end address + fputc(ctx->end_pos & 0xff, ctx->dst_file); + fputc(ctx->end_pos >> 8, ctx->dst_file); + } + + // Do some sanity checks + if (ctx->load_addr + packed_size + ctx->margin < ctx->output_end) { + if (ctx->load_addr + packed_size + ctx->margin >= ctx->output_begin) { + fprintf(stderr,"WARNING: packed file location collides with depacked data location\n"); + if (ctx->exit_on_warn) exit(EXIT_FAILURE); + } + } + if (packed_size > ctx->output_end - ctx->output_begin) { + fprintf(stderr,"WARNING: compressed file is bigger than original\n"); + if (ctx->exit_on_warn) exit(EXIT_FAILURE); + } + if ((ctx->load_addr > 0xd000 && ctx->load_addr < 0xe000) || (ctx->load_addr + packed_size > 0xd000 && ctx->load_addr + packed_size < 0xe000)) { + fprintf(stderr,"WARNING: resulting file has parts in IO-range $d000-$dfff\n"); + if (ctx->exit_on_warn) exit(EXIT_FAILURE); + } + if (ctx->output_type == OUTPUT_BITFIRE) { + printf("filetype: bitfire\n"); + } else { + printf("filetype: level\n"); + } + } else if(ctx->output_type == OUTPUT_SFX) { + packed_size -= 26; + data_addr = decruncher_size + packed_size - 0xff + 0x800 + 24; + + // Set up depacker values + fseek(ctx->dst_file, 0x2e, SEEK_SET); + fputc((packed_size >> 8) + 1, ctx->dst_file); + + fseek(ctx->dst_file, 0x31, SEEK_SET); + fputc(data_addr & 0xff, ctx->dst_file); + fputc(data_addr >> 8, ctx->dst_file); + + fseek(ctx->dst_file, 0x43, SEEK_SET); + fputc((0x10000 - packed_size) & 0xff, ctx->dst_file); + + fseek(ctx->dst_file, 0x6a, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx->dst_file); + fseek(ctx->dst_file, 0xd3, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx->dst_file); + fseek(ctx->dst_file, 0x118, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx->dst_file); + + fseek(ctx->dst_file, 0x72, SEEK_SET); + fputc(ctx->output_begin & 0xff, ctx->dst_file); + fputc(ctx->output_begin >> 8, ctx->dst_file); + + printf("start address: $%04x (%d)\n", ctx->start_addr, ctx->start_addr); + printf("final size: $%04x (%d)\n", packed_size + decruncher_size, packed_size + decruncher_size); + printf("filetype: sfx\n"); + } else { + } + + fclose(ctx->dst_file); + + if (ctx->checksum) { + checksum = 0; + for (i = ctx->output_begin; i < ctx->output_end; i++) { + checksum ^= ctx->src_data[i]; + } + printf("checksum: $%02x\n", checksum); + } + + // Display some statistics gathered in the process + if(ctx->show_stats) print_statistics(ctx, stdout); + return EXIT_SUCCESS; +} + +/****************************************************************************** + * The main function + ******************************************************************************/ +int +#ifdef _MSC_VER +__cdecl +#endif +main(int argc, char *argv[]) { + const char *program_name; + unsigned name_length; + lz_context ctx; + + memset(&ctx.stats, 0, sizeof ctx.stats); + // Parse the command line + program_name = *argv; + + ctx.iterations = 0; + ctx.output_name = NULL; + ctx.cut_addr_first = 0; + ctx.cut_addr_last = INT_MAX; + ctx.emit_offset_tables = NULL; + ctx.show_stats = false; + + ctx.is_cbm = true; + ctx.show_trace = false; + ctx.output_type = OUTPUT_LEVEL; + ctx.full_dict = false; + ctx.overlap = false; + ctx.header_size = 0; + ctx.checksum = false; + ctx.exit_on_warn = false; + ctx.load_addr = -1; + ctx.depack_to = -1; + ctx.start_addr = -1; + ctx.relocate_to = -1; + ctx.write_tables = false; + ctx.offset_lengths = false; + + while(++argv, --argc) { + if(argc >= 2 && !strcmp(*argv, "-o")) { + ctx.output_name = *++argv; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--sfx")) { + ctx.start_addr = read_number(*++argv); + ctx.output_type = OUTPUT_SFX; + ctx.write_tables = true; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--load-addr")) { + ctx.load_addr = read_number(*++argv); + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--depack-to")) { + ctx.depack_to = read_number(*++argv); + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--relocate-to")) { + ctx.relocate_to = read_number(*++argv); + --argc; + } else if(!strcmp(*argv, "--level")) { + ctx.output_type = OUTPUT_LEVEL; + } else if(!strcmp(*argv, "--bitfire")) { + ctx.output_type = OUTPUT_BITFIRE; + } else if(argc >= 3 && !strcmp(*argv, "--cut-input-addr")) { + ctx.cut_addr_first = read_number(*++argv); + ctx.cut_addr_last = read_number(*++argv); + if(ctx.cut_addr_first > ctx.cut_addr_last) + fatal("last must be larger than first"); + argc -= 2; + } else if(!strcmp(*argv, "--full-dict")) { + ctx.full_dict = true; + } else if(!strcmp(*argv, "--overlap")) { + ctx.overlap = true; + } else if(!strcmp(*argv, "--best-offset-tables")) { + ctx.iterations = 1; + } else if(argc >= 2 && !strcmp(*argv, "--offset-lengths")) { + ctx.offset_lengths = true; + if(!parse_offset_lengths(&ctx, *++argv)) + break; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--emit-offset-tables")) { + ctx.emit_offset_tables = *++argv; + --argc; + } else if(!strcmp(*argv, "--include-tables")) { + ctx.write_tables = true; + } else if(!strcmp(*argv, "--statistics")) { + ctx.show_stats = true; + } else if(!strcmp(*argv, "--trace-coding")) { + ctx.show_trace = true; + } else if(!strcmp(*argv, "--binfile")) { + ctx.is_cbm = false; + } else if(!strcmp(*argv, "--checksum")) { + ctx.checksum = true; + } else if(!strcmp(*argv, "--exit-on-warn")) { + ctx.exit_on_warn = true; + } else { + break; + } + } + + if (!ctx.offset_lengths) parse_offset_lengths(&ctx, DEFAULT_LENGTHS); + + if(ctx.emit_offset_tables) { + write_offset_tables(ctx.emit_offset_tables); + // It allowed to just generate the offset tables + if(!argc) + return EXIT_SUCCESS; + } + + if(argc != 1) { + fprintf ( + stderr, + "syntax: %s\n" + "\t[-o output.lz selects output file\n" + "\t[--overlap] allow overlap and disable in place depacking\n" + "\t[--sfx startaddr] create a sfx with given startaddress\n" + "\t[--level] create a level packed file\n" + "\t[--bitfire] create a file compatible with bitfire\n" + "\t[--full-dict] allow matches over whole file\n" + "\t[--cut-input-addr first last] only pack selected part of file\n" + "\t[--load-addr addr] change load address of packed file\n" + "\t[--depack-to addr] change depack address of file\n" + "\t[--relocate-to addr] change load address of file, prior to packing\n" + "\t[--binfile] file has no 2 byte load-address (--relocate-to can help)\n" + "\t[--checksum] print eor checksum over file\n" + "\t[--exit_on_warn] exit with error instead of giving a warning\n" + "\t[--best-offset-tables] evaluate best offset tables, slow\n" + "\t[--include-tables] add own tables to packed sfx\n" + "\t[--offset-lengths s1/s2/s3/s4:l1/l2/l3/l4] use alternative offset lengths\n" + "\t[--emit-offset-tables tables.asm] spit out offset table\n" + "\t[--statistics] print some stats\n" + "\t[--trace-coding] print more gibberish\n" + "\t{input-file} the input file\n", + program_name + ); + return EXIT_FAILURE; + } + + ctx.input_name = *argv; + + if (!ctx.is_cbm) { + if (ctx.relocate_to < 0) { + fatal("a load address is needed via the --relocate-to switch"); + } + } + + if (ctx.output_type == OUTPUT_SFX) { + if (!ctx.overlap) { + ctx.overlap = true; + //fprintf(stderr,"ignoring --overlap option for filetype --sfx"); + } + if (ctx.depack_to >= 0) { + fatal("option --depack-to not supported with --sfx"); + } + if (ctx.load_addr >= 0) { + fatal("option --load-addr not supported with --sfx"); + } + if (ctx.relocate_to >= 0) { + fatal("option --relocate-to not supported with --sfx"); + } + } + + if (ctx.output_type == OUTPUT_BITFIRE) { + if (ctx.offset_lengths) { + fatal("option --offset_lengths not supported with --bitfire"); + } + if (ctx.write_tables) { + fatal("option --write_tables not supported with --bitfire"); + } + if (ctx.iterations > 0) { + fatal("option --best_offset_tables not supported with --bitfire"); + } + } + + // If necessary generate output file by substituting the + // extension for .lz + if(!ctx.output_name) { + static const char extension[] = ".lz"; + name_length = strlen(ctx.input_name); + + ctx.output_name = malloc(name_length + 1 + sizeof extension); + + memcpy(ctx.output_name, ctx.input_name, name_length); + memcpy(ctx.output_name + name_length, extension, sizeof extension); + } + + return crunch(&ctx); +} diff --git a/loader/tools/cc1541/LICENSE.txt b/loader/tools/cc1541/LICENSE.txt new file mode 100644 index 0000000..7689e35 --- /dev/null +++ b/loader/tools/cc1541/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2008-2022 JackAsser, Krill, Claus, Björn Esser + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/loader/tools/cc1541/Makefile b/loader/tools/cc1541/Makefile new file mode 100755 index 0000000..7fd703a --- /dev/null +++ b/loader/tools/cc1541/Makefile @@ -0,0 +1,102 @@ +CC1541_CFLAGS = -std=c99 -pipe -O2 -Wall -Wextra -pedantic + +ALL_TARGETS = cc1541 + +ifneq ($(ENABLE_MAN),) +ALL_TARGETS += man +endif + +ifneq ($(ENABLE_WERROR),) +CC1541_CFLAGS += -Werror +endif + +override CFLAGS := $(CC1541_CFLAGS) $(CFLAGS) + +prefix ?= /usr/local +bindir ?= $(prefix)/bin +mandir ?= $(prefix)/share/man + +INSTALL ?= install + +VERSION := $(shell grep 'define VERSION' cc1541.c | cut -d\" -f2) + +CC1541_SRC := Makefile transwarp\ v0.84.prg transwarp\ v0.86.prg $(wildcard *.c *.h *.in *.sln *.vcxproj* LICENSE* README*) + +all: $(ALL_TARGETS) + +cc1541: cc1541.c + +cc1541.1.txt: cc1541.1.txt.in + sed -e "s/@@VERSION@@/$(VERSION)/g" < cc1541.1.txt.in > cc1541.1.txt + +cc1541.1: cc1541.1.txt + a2x -d manpage -f manpage cc1541.1.txt + +man: cc1541.1 + +test_cc1541: test_cc1541.c + +check: cc1541 test_cc1541 + ./test_cc1541 ./cc1541 + +test: check + +install: all + $(INSTALL) -Dpm 0755 ./cc1541 $(DESTDIR)$(bindir)/cc1541 +ifneq ($(ENABLE_MAN),) + $(INSTALL) -Dpm 0644 ./cc1541.1 $(DESTDIR)$(mandir)/man1/cc1541.1 +endif + +cc1541-$(VERSION).tar: $(CC1541_SRC) + rm -rf cc1541-$(VERSION)/ *~ README.md.T + mkdir -p cc1541-$(VERSION) + cp -a $(CC1541_SRC) cc1541-$(VERSION)/ + chmod 0644 cc1541-$(VERSION)/* + tar cf cc1541-$(VERSION).tar cc1541-$(VERSION)/ + rm -rf cc1541-$(VERSION)/ + +cc1541-$(VERSION).tar.bz2: cc1541-$(VERSION).tar + bzip2 -9cz < cc1541-$(VERSION).tar > cc1541-$(VERSION).tar.bz2 + +cc1541-$(VERSION).tar.gz: cc1541-$(VERSION).tar + gzip -9c < cc1541-$(VERSION).tar > cc1541-$(VERSION).tar.gz + +cc1541-$(VERSION).tar.xz: cc1541-$(VERSION).tar + xz -ce < cc1541-$(VERSION).tar > cc1541-$(VERSION).tar.xz + +cc1541-$(VERSION).zip: $(CC1541_SRC) + rm -rf cc1541-$(VERSION)/ *~ README.md.T + mkdir -p cc1541-$(VERSION) + cp -a $(CC1541_SRC) cc1541-$(VERSION)/ + chmod 0644 cc1541-$(VERSION)/* + zip -9r cc1541-$(VERSION).zip cc1541-$(VERSION)/ + rm -rf cc1541-$(VERSION)/ + +dist-bz2: cc1541-$(VERSION).tar.bz2 +dist-gz: cc1541-$(VERSION).tar.gz +dist-xz: cc1541-$(VERSION).tar.xz +dist-zip: cc1541-$(VERSION).zip +dist-all: dist-bz2 dist-gz dist-xz dist-zip + +dist: dist-gz dist-zip + +dist-check: dist + tar xf cc1541-$(VERSION).tar.gz + $(MAKE) -C cc1541-$(VERSION)/ all check + rm -rf cc1541-$(VERSION)/ + +codestyle: cc1541.c test_cc1541.c + astyle --style=kr -n -s -z2 cc1541.c test_cc1541.c + +wrap: cc1541.1.txt.in LICENSE.txt README.md + for f in $^; do \ + fold -s -w 70 < $$f | perl -pe 's/[\t\040]+$$//' > $$f.T; \ + mv -f $$f.T $$f; \ + done + +clean: + rm -rf cc1541-$(VERSION)/ *~ README.md.T *.o *.orig cc1541 test_cc1541 cc1541-$(VERSION).* cc1541.1 cc1541.1.txt + +.PHONY: all check clean codestyle dist dist-all dist-bz2 dist-check dist-gz dist-xz dist-zip install man test wrap + +.NOTPARALLEL: cc1541-$(VERSION).tar cc1541-$(VERSION).zip diff --git a/loader/tools/cc1541/README.md b/loader/tools/cc1541/README.md new file mode 100755 index 0000000..bcf816c --- /dev/null +++ b/loader/tools/cc1541/README.md @@ -0,0 +1,147 @@ +# README # + +This is cc1541, a tool for creating Commodore 1541 Floppy disk +images in D64, D71 or D81 format with custom sector interleaving +etc. Also supports extended tracks 35-40 using either SPEED DOS or +DOLPHIN DOS BAM-formatting. Furthermore supports writing Transwarp +disk images for the fantastically fast loader by Krill. + +Originally written by JackAsser, with improvements and fixes by +Krill, some more improvements by Claus and further improvements by +Björn Esser. + +The program is provided under the terms of the MIT license, please +refer to the included LICENSE.txt file for its terms and conditions. + +The public source code repository can be found here: +https://bitbucket.org/PTV_Claus/cc1541/src/master/ + +## Usage examples ## + +* "cc1541 -h" shows the command line help +* "cc1541 image.d64" lists the content of image.d64 +* "cc1541 -f program -w program.prg image.d64" adds the file + program.prg to image.d64 (or creates the image if it does not + exist yet) using the name "program" +* "cc1541 -f program1 -w program1.prg -f program2 -w program2.prg + image.d64" adds two files under the names program1 and program2 +* "cc1541 -s 4 -f program -w program.prg image.d64" writes a file + with a dedicated sector interleave for a fastloader (the best + value depends on the used fastloader and its configuration) +* "cc1541 -f game -W game.prg -f 'transwarp v0.86' -w transwarp.prg + Transwarp-Game.d64" creates a Transwarp disk image with the + required boot file. +* "cc1541 -T DEL -f ---------------- -L image.d64" creates a DEL + entry as separator. + +## Version history ## + +v4.0 + +* The default handling for large tail gaps has been removed, as it + does not provide any advantage and was only there to be as close + as possible to CBM DOS behaviour. In fact, it actually slows down + fast loaders. This warrants a major version number increase. +* -H switch added to set BAM messages at BAM offset $ab +* -F with negative values now specifies a track skew +* -B cannot be used with transwarp files anymore, as the loader + relies on correct block sizes in the directory +* Bugfix: the BAM allocation for SPEED DOS and DOLPHIN DOS was + wrong (mixed up between the two and also shifted by 4 bytes) +* Bugfix: Data from unused blocks could leak into the last block + of a new file behind the actual file data + +v3.4 + +* Support for Transwarp 0.86 and later +* Commandline option -R added for restoring deleted or formatted + files +* -L switch added to create directory entries without writing a + file +* -a switch added to print a cc1541 command line that re-creates + the directory art of the given disk image +* -T now also allows a number as parameter +* Files with file type DEL will now actually be written, use + -L if you do not want that +* Improved verbose allocation printout +* More robustness against invalid t/s links +* Added support for underscore in PETSCII filenames +* Bugfix: no endless loops for cyclic directory chains anymore + +v3.3 + +* Transwarp encoding added +* Verbose mode displays file allocation on unmodified images +* Optimised non-standard interleave scheme +* Added unicode printout option for PETSCII, special thanks to + Jonas Minnberg for helping with Linux support and inverse + characters! +* Output file is not touched when no change is requested +* Bugfix: Fixed crash on write protected output file +* Bugfix: Spacing in directory printout was not correct when + file size is more than 999 blocks + +v3.2 + +* -N switch added to allow duplicate file names +* Trying to create loop file with the same name as the referenced + file now throws an error, if not -N is also specified +* Changed default next file first sector on new track to 0 to align + with Commodore DOS +* Interleave violations are now shown in block allocation printout +* Block allocation for loop files is not displayed anymore when + referenced file was also written +* Bugfix: Fixed file index printout for block allocation +* Bugfix: Correct interleave is now displayed for loop files when + referenced file was also written + +v3.1 + +* Bugfix: overwriting files did not work correctly when there was a + free slot in the directory before the file to overwrite +* Bugfix: Filetype, open and protected flags were not reset after + writing a file +* Bugfix: default disk name and id were wrong in command line help + +v3.0 + +* ASCII to PETSCII conversion added, this breaks backward + compatibility (and therefore warrants a major version increase)! +* Support for D81 images +* Default printout is now a full directory similar to how it would be + displayed on a Commodore machine +* -v switch added for verbose output of file and block allocation +* -M switch added to specify number of characters for filename hash + calculation for latest Krill loader +* -m switch added to skip filename collision check +* -B switch added to allow setting the displayed file size +* -o switch added to prevent overwriting of existing files on an image +* -V switch added to validate images before editing them +* -T switch added to allow setting the file type +* -O switch added to allow setting the open flag +* -P switch added to allow setting the protected flag +* Hex escapes are now also allowed for disk name and ID +* When no disk file name is provided, only the base name of the input + file is used as disk file name instead of the full path +* Bugfix: fixed memory access issue for filenames with 16 letters +* Bugfix: fixed endless loop when reaching track 53 on a D71 +* Bugfix: fixed that (shadow) directory track would be used for data + when the shadow directory is a neighbour of the actual directory +* Bugfix: G64 output is now an optional additional output using -g, + avoiding the utterly broken reading of G64 files +* Bugfix: loop files have actual file size per default instead of 0 +* Bugfix: printouts to stderr and stdout are more consistent now + +v2.0 + +* The first version with a release number +* All existing modifications consolidated (hopefully) +* G64 output dependent on output file name instead of a source code + define +* Converted to ANSI C99 +* MSVC build files added +* getopt removed +* Simple test suite added +* Bugfix: hex escape was not considered for file overwrite detection +* Bugfix: first sector per track was ignored for track 1 +* Bugfix: default sector interleave was ignored for first file diff --git a/loader/tools/cc1541/cc1541 b/loader/tools/cc1541/cc1541 new file mode 100755 index 0000000..12077c8 Binary files /dev/null and b/loader/tools/cc1541/cc1541 differ diff --git a/loader/tools/cc1541/cc1541.1.txt.in b/loader/tools/cc1541/cc1541.1.txt.in new file mode 100755 index 0000000..b509058 --- /dev/null +++ b/loader/tools/cc1541/cc1541.1.txt.in @@ -0,0 +1,209 @@ += cc1541(1) +Björn Esser +:doctype: manpage +:Author: JackAsser, Krill, Claus, Björn Esser +:Email: besser82@fedoraproject.org +:manvolnum: 1 +:manversion: v@@VERSION@@ +:manmanual: cc1541 manual +:mansource: cc1541 +:man-linkstyle: pass:[blue R < >] + +== Name + +cc1541 - A tool for creating Commodore 1541 Floppy disk images +in D64, D71 or D81 format with custom sector interleaving etc. +Also supports extended tracks 35-40 using either SPEED DOS or +DOLPHIN DOS BAM-formatting. + +== Synopsis + +*cc1541* [_options_] image.[_d64|d71|d81_] + +== Options + +*-n diskname*:: + Disk name, default='cc1541'. + +*-i id*:: + Disk ID, default='00 2a'. + +*-H message*:: + Hidden BAM message. Only for D64 (up to 85 chars) or SPEED DOS +(up to 20 chars). + +*-w localname*:: + Write local file to disk, if filename is not set then the local +name is used. After file written, the filename is unset. + +*-W localname*:: + Like -w, but encode file in Transwarp format. + +*-K key*:: + Set an encryption key for Transwarp files, a string of up to 29 + characters. + +*-f filename*:: + Use filename as name when writing next file, use prefix _#_ to +include arbitrary PETSCII characters (e.g. -f "START#a0,8,1"). + +*-o*:: + Do not overwrite if file with same name exists already. + +*-V*:: + Do not modify image unless it is in valid CBM DOS format. + +*-T filetype*:: + Filetype for next file, allowed parameters are PRG, SEQ, USR, REL +and DEL, or a decimal number between 0 and 255. Default is PRG. + +*-P*:: + Set write protect flag for next file. + +*-O*:: + Set open flag for next file. + +*-N*:: + Force creation of a new directory entry, even if a file with the + same name exists already. + +*-l filename*:: + Write loop file (an additional dir entry) to existing file to disk, +set filename with -f. + +*-L*:: + Add dir entry without writing file (track and sector will be 0), +requires a filename given with -f. + +*-B numblocks*:: + Write the given value as file size in blocks to the directory for +the next file. + +*-M numchars*:: + Hash computation maximum filename length, this must match loader +option FILENAME_MAXLENGTH in Krill's loader. Default is 16. + +*-m*:: + Ignore filename hash collisions, without this switch a collision +results in an error. + +*-d track*:: + Maintain a shadow directory (copy of the actual directory without a +valid BAM). + +*-t*:: + Use directory track to also store files (makes -x useless) (default no). + +*-u numblocks*:: + When using -t, amount of dir blocks to leave free (default=2). + +*-x*:: + Don't split files over directory track hole (default split files). + +*-F*:: + Next file first sector on a new track (default=0). Any negative +value assumes aligned tracks and uses current sector + interleave - value. +After each file, the value falls back to the default. Not applicable +for D81. + +*-S value*:: + Default sector interleave, default=10. Not applicable for D81. + +*-s value*:: + Next file sector interleave, valid after each file. The interleave value +falls back to the default value set by -S after the first sector of the +next file. Not applicable for D81. + +*-e*:: + Start next file on an empty track (default start sector is current +sector plus interleave). + +*-E*:: + Try to fit file on a single track. + +*-r track*:: + Restrict next file blocks to the specified track or higher. + +*-b sector*:: + Set next file beginning sector to the specified value. Not +applicable for D81. + +*-c*:: + Save next file cluster-optimized (d71 only). + +*-4*:: + Use tracks 35-40 with SPEED DOS BAM formatting. + +*-5*:: + Use tracks 35-40 with DOLPHIN DOS BAM formatting. + +*-R level*:: + Try to restore deleted and formatted files. +level 0: Only restore all dir entries without touching any t/s links. +level 1: Fix dir entries for files with valid t/s chains. +level 2: Also add wild sector chains with valid t/s chains. +level 3: Also fix dir entries with invalid t/s chains. +level 4: Also add and fix wild invalid t/s chains. +level 5: Also add reasonable wild single blocks. + +*-g filename*:: + Write additional g64 output file with given name. + +*-a*:: +Print command line options that would create the same directory as the +one in the given image (for directory art import). + +*-U mapping*:: + Print PETSCII as Unicode (requires Unicode 13.0 font, e.g. UNSCII). +Use mapping 0 for ASCII output, 1 for upper case, 2 for lower case, +default is 0. + +*-q*:: + Be quiet. + +*-v*:: + Be verbose. + +*-h*:: + Print command line help. + +== Exit status + +*0*:: + Success. + +*-1*:: + Failure. + +== Resources + +Project web site: https://bitbucket.org/PTV_Claus/cc1541/src + +== Bug Reports + +Please report bugs to: +https://bitbucket.org/PTV_Claus/cc1541/issues/new + +== License + +Copyright (C) 2008-2021 {author}. + + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/loader/tools/cc1541/cc1541.c b/loader/tools/cc1541/cc1541.c new file mode 100755 index 0000000..9e0fd08 --- /dev/null +++ b/loader/tools/cc1541/cc1541.c @@ -0,0 +1,4880 @@ +/******************************************************************************* +* Copyright (c) 2008-2022 JackAsser, Krill, Claus, Björn Esser +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*******************************************************************************/ + +#define VERSION "4.0" + +#define _CRT_SECURE_NO_WARNINGS /* avoid security warnings for MSVC */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +#ifdef _WIN32 +#include +#define FILESEPARATOR '\\' +#else +#define FILESEPARATOR '/' +#endif + +#define DIRENTRIESPERBLOCK 8 +#define DIRTRACK_D41_D71 18 +#define DIRTRACK_D81 40 +#define SECTORSPERTRACK_D81 40 +#define MAXNUMFILES_D81 ((SECTORSPERTRACK_D81 - 3) * DIRENTRIESPERBLOCK) +#define DIRENTRYSIZE 32 +#define BLOCKSIZE 256 +#define BLOCKOVERHEAD 2 +#define TRACKLINKOFFSET 0 +#define SECTORLINKOFFSET 1 +#define FILETYPEOFFSET 2 +#define FILETYPEDEL 0 +#define FILETYPESEQ 1 +#define FILETYPEPRG 2 +#define FILETYPEUSR 3 +#define FILETYPEREL 4 +#define FILETYPETRANSWARPMASK 0x100 +#define FILETRACKOFFSET 3 +#define FILESECTOROFFSET 4 +#define FILENAMEOFFSET 5 +#define FILENAMEMAXSIZE 16 +#define FILENAMEEMPTYCHAR (' ' | 0x80) +#define BAMMESSAGEOFFSET 0xab +#define BAMMESSAGEMAXSIZE 0x100-BAMMESSAGEOFFSET +#define TRANSWARPSIGNATROFFSLO 21 +#define TRANSWARPSIGNATURELO 'T' +#define TRANSWARPSIGNATROFFSHI 22 +#define TRANSWARPSIGNATUREHI 'W' +#define DIRDATACHECKSUMOFFSET 23 +#define TRANSWARPTRACKOFFSET 24 +#define FILECHECKSUMOFFSET 25 +#define LOADADDRESSLOOFFSET 26 +#define LOADADDRESSHIOFFSET 27 +#define ENDADDRESSLOOFFSET 28 +#define ENDADDRESSHIOFFSET 29 +#define FILEBLOCKSLOOFFSET 30 +#define FILEBLOCKSHIOFFSET 31 +#define D64NUMBLOCKS (664 + 19) +#define D64SIZE (D64NUMBLOCKS * BLOCKSIZE) +#define D64SIZE_EXTENDED (D64SIZE + 5 * 17 * BLOCKSIZE) +#define D71SIZE (D64SIZE * 2) +#define D81SIZE (D81NUMTRACKS * SECTORSPERTRACK_D81 * BLOCKSIZE) +#define D64NUMTRACKS 35 +#define D64NUMTRACKS_EXTENDED (D64NUMTRACKS + 5) +#define D71NUMTRACKS (D64NUMTRACKS * 2) +#define D81NUMTRACKS 80 +#define BAM_OFFSET_SPEED_DOS 0xc0 +#define BAM_OFFSET_DOLPHIN_DOS 0xac +#define DIRSLOTEXISTS 0 +#define DIRSLOTFOUND 1 +#define DIRSLOTNOTFOUND 2 +/* for sector chain analysis */ +#define UNALLOCATED 0 /* unused as of now */ +#define ALLOCATED 1 /* part of a valid sector chain */ +#define FILESTART 2 /* analysed to be the start of a sector chain */ +#define FILESTART_TRUNCATED 3 /* analysed to be the start of a sector chain, was truncated */ +#define POTENTIALLYALLOCATED 4 /* currently being analysed */ +/* error codes for sector chain validation */ +#define VALID 0 /* valid chain */ +#define ILLEGAL_TRACK 1 /* ends with illegal track pointer */ +#define ILLEGAL_SECTOR 2 /* ends with illegal sector pointer */ +#define LOOP 3 /* loop in current chain */ +#define COLLISION 4 /* collision with other file */ +#define CHAINED 5 /* ends at another file start */ +#define CHAINED_TRUNCATED 6 /* ends at start of a truncated file */ +#define FIRST_BROKEN 7 /* issue already in first sector */ +/* undelete levels */ +#define RESTORE_DIR_ONLY 0 /* Only restore all dir entries without touching any t/s links */ +#define RESTORE_VALID_FILES 1 /* Fix dir entries for files with valid t/s chains */ +#define RESTORE_VALID_CHAINS 2 /* Also add wild sector chains with valid t/s chains */ +#define RESTORE_INVALID_FILES 3 /* Also fix dir entries with invalid t/s chains */ +#define RESTORE_INVALID_CHAINS 4 /* Also add and fix wild invalid t/s chains */ +#define RESTORE_INVALID_SINGLES 5 /* Also include single block files */ +/* error codes for directory */ +#define DIR_OK 0 +#define DIR_ILLEGAL_TS 1 +#define DIR_CYCLIC_TS 2 + +#define TRANSWARP "TRANSWARP" +#define TRANSWARPBASEBLOCKSIZE 0xc0 +#define TRANSWARPBUFFERBLOCKSIZE 0x1f +#define TRANSWARPBLOCKSIZE (TRANSWARPBASEBLOCKSIZE + TRANSWARPBUFFERBLOCKSIZE) +#define TRANSWARPKEYSIZE 29 /* 232 bits */ +#define TRANSWARPKEYHASHROUNDS 33 + +/* Table for conversion of uppercase PETSCII to Unicode */ +static unsigned int p2u_uppercase_tab[256] = { + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + ' ', '!', '\"', '#', '$', '%', '&', 0x2019, '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 0xa3, ']', 0x2191, 0x2190, + 0x1fb79, 0x2660, 0x1fb72, 0x1fb78, 0x1fb77, 0x1fb76, 0x1fb7a, 0x1fb71, 0x1fb74, 0x256e, 0x2570, 0x256f, 0x1fb7c, 0x2572, 0x2571, 0x1fb7d, + 0x1fb7e, 0x25cf, 0x1fb7b, 0x2665, 0x1fb70, 0x256d, 0x2573, 0x25cb, 0x2663, 0x1fb75, 0x2666, 0x253c, 0x1fb8c, 0x2502, 0x3c0, 0x25e5, + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + ' ', 0x258c, 0x2584, 0x2594, 0x2581, 0x258e, 0x1fb95, 0x1fb75, 0x1fb8f, 0x25e4, 0x1fb75, 0x251c, 0x2597, 0x2514, 0x2510, 0x2581, + 0x250c, 0x2534, 0x252c, 0x2524, 0x258e, 0x258d, 0x2590, 0x2594, 0x2580, 0x2583, 0x1fb7f, 0x2596, 0x259d, 0x2518, 0x2598, 0x259a, + 0x2500, 0x2660, 0x1fb72, 0x2500, 0x1fb77, 0x1fb76, 0x1fb7a, 0x1fb71, 0x1fb74, 0x256e, 0x2570, 0x256f, 0x1fb7c, 0x2572, 0x2571, 0x1fb7d, + 0x1fb7e, 0x25cf, 0x1fb7b, 0x2665, 0x1fb70, 0x256d, 0x2573, 0x25cb, 0x2663, 0x1fb75, 0x2666, 0x253c, 0x1fb8c, 0x2502, 0x3c0, 0x25e5, + ' ', 0x258c, 0x2584, 0x2594, 0x2581, 0x258e, 0x1fb95, 0x1fb75, 0x1fb8f, 0x25e4, 0x1fb75, 0x251c, 0x2597, 0x2514, 0x2510, 0x2581, + 0x250c, 0x2534, 0x252c, 0x2524, 0x258e, 0x258d, 0x1fb88, 0x1fb82, 0x1fb83, 0x2583, 0x1fb7f, 0x2596, 0x259d, 0x2518, 0x2598, 0x3c0 +}; + +/* Table for conversion of lowercase PETSCII to Unicode */ +static unsigned int p2u_lowercase_tab[256] = { + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + ' ', '!', '\"', '#', '$', '%', '&', 0x2019, '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', 0xa3, ']', 0x2191, 0x2190, + 0x1fb79, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x253c, 0x1fb8c, 0x2502, 0x1fb95, 0x1fb98, + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', + ' ', 0x258c, 0x2584, 0x2594, 0x2581, 0x258e, 0x1fb95, 0x1fb75, 0x1fb8f, 0x1fb99, 0x1fb75, 0x251c, 0x2597, 0x2514, 0x2510, 0x2581, + 0x250c, 0x2534, 0x252c, 0x2524, 0x258e, 0x258d, 0x2590, 0x2594, 0x2580, 0x2583, 0x1fb7f, 0x2713, 0x259d, 0x2518, 0x2598, 0x259a, + 0x2500, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x253c, 0x1fb8c, 0x2502, 0x1fb95, 0x1fb98, + ' ', 0x258c, 0x2584, 0x2594, 0x2581, 0x258e, 0x1fb95, 0x1fb75, 0x1fb8f, 0x1fb99, 0x1fb75, 0x251c, 0x2597, 0x2514, 0x2510, 0x2581, + 0x250c, 0x2534, 0x252c, 0x2524, 0x258e, 0x258d, 0x1fb88, 0x1fb82, 0x1fb83, 0x2583, 0x2713, 0x2596, 0x259d, 0x2518, 0x2598, 0x1fb96 +}; + +typedef struct { + const unsigned char* alocalname; /* local file name or name of loop file in ASCII */ + unsigned char plocalname[FILENAMEMAXSIZE]; /* loop file in PETSCII */ + unsigned char pfilename[FILENAMEMAXSIZE]; /* disk file name in PETSCII */ + int direntryindex; + int direntrysector; + int direntryoffset; + int sectorInterleave; + int first_sector_new_track; + int track; + int sector; + int nrSectors; + int nrSectorsShown; + int filetype; + int mode; + int force_new; + int size; + int last_track; + bool have_key; + unsigned char key[TRANSWARPKEYSIZE]; +} imagefile; + +enum mode { + MODE_BEGINNING_SECTOR_MASK = 0x003f, /* 6 bits */ + MODE_MIN_TRACK_MASK = 0x0fc0, /* 6 bits */ + MODE_MIN_TRACK_SHIFT = 6, + MODE_SAVETOEMPTYTRACKS = 0x1000, + MODE_FITONSINGLETRACK = 0x2000, + MODE_SAVECLUSTEROPTIMIZED = 0x4000, + MODE_LOOPFILE = 0x8000, + MODE_TRANSWARPBOOTFILE = 0x10000, + MODE_NOFILE = 0x20000 +}; + +typedef enum { + IMAGE_D64, + IMAGE_D64_EXTENDED_SPEED_DOS, + IMAGE_D64_EXTENDED_DOLPHIN_DOS, + IMAGE_D71, + IMAGE_D81 +} image_type; + +static const char *filetypename_uc[] = { + "DEL", "SEQ", "PRG", "USR", "REL", "CBM", "???", "???", + "???", "???", "???", "???", "???", "???", "???", "???" +}; + +static const char *filetypename_lc[] = { + "del", "seq", "prg", "usr", "rel", "cbm", "???", "???", + "???", "???", "???", "???", "???", "???", "???", "???" +}; + +static const char *error_name[] = { + "no error", "illegal track", "illegal sector", "loop", "collision", "chained", "chained", "first block broken" +}; + +static const char *dir_error_string[] = { + "no error", "illegal track or sector in directory sector chain", "cycle in directory sector chain" +}; + +static const int +sectors_per_track[] = { + /* 1-17 */ 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + /* 18-24 */ 19,19,19,19,19,19,19, + /* 25-30 */ 18,18,18,18,18,18, + /* 31-35 */ 17,17,17,17,17, + /* 36-52 */ 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + /* 53-59 */ 19,19,19,19,19,19,19, + /* 60-65 */ 18,18,18,18,18,18, + /* 66-70 */ 17,17,17,17,17 +}; + +static const int +sectors_per_track_extended[] = { + /* 1-17 */ 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + /* 18-24 */ 19,19,19,19,19,19,19, + /* 25-30 */ 18,18,18,18,18,18, + /* 31-35 */ 17,17,17,17,17, + /* 36-40 */ 17,17,17,17,17, + /* 41-57 */ 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + /* 58-64 */ 19,19,19,19,19,19,19, + /* 65-70 */ 18,18,18,18,18,18, + /* 71-75 */ 17,17,17,17,17, + /* 76-80 */ 17,17,17,17,17 +}; + +static int quiet = 0; /* global quiet flag */ +static int verbose = 0; /* global verbose flag */ +static int num_files = 0; /* number of files to be written provided by the user */ +static int max_hash_length = 16; /* number of bytes of the filenames to calculate the hash over */ +static int unicode = 0; /* which unicode mapping to use: 0 = none, 1 = upper case, 2 = lower case */ +static int modified = 0; /* image needs to be written */ +static int dir_error = DIR_OK; /* directory has an error */ + +/* Prints the command line help */ +static void +usage() +{ + printf("\n*** This is cc1541 version " VERSION " built on " __DATE__ " ***\n\n"); + printf("Usage: cc1541 [options] image.[d64|d71|d81]\n\n"); + printf("-n diskname Disk name, default='cc1541'.\n"); + printf("-i id Disk ID, default='00 2a'.\n"); + printf("-H message Hidden BAM message. Only for D64 (up to 85 chars) or SPEED DOS\n"); + printf(" (up to 20 chars).\n"); + printf("-w localname Write local file to disk, if filename is not set then the\n"); + printf(" local name is used. After file written, the filename is unset.\n"); + printf("-W localname Like -w, but encode file in Transwarp format.\n"); + printf(" Provide Transwarp bootfile as last file using\n"); + printf(" \"-f 'transwarp vX.YZ' -w 'transwarp vX.YZ.prg'\"\n"); + printf("-K key Set an encryption key for Transwarp files, a string of up to 29\n"); + printf(" characters.\n"); + printf("-f filename Use filename as name when writing next file, use prefix # to\n"); + printf(" include arbitrary PETSCII characters (e.g. -f \"start#a0,8,1\").\n"); + printf("-o Do not overwrite if file with same name exists already.\n"); + printf("-V Do not modify image unless it is in valid CBM DOS format.\n"); + printf("-T filetype Filetype for next file, allowed parameters are PRG, SEQ, USR, REL\n"); + printf(" and DEL, or a decimal number between 0 and 255. Default is PRG.\n"); + printf("-P Set write protect flag for next file.\n"); + printf("-O Set open flag for next file.\n"); + printf("-N Force creation of a new directory entry, even if a file with the\n"); + printf(" same name exists already.\n"); + printf("-l filename Write loop file (an additional dir entry) to existing file to\n"); + printf(" disk, set filename with -f.\n"); + printf("-L Add dir entry without writing file (track and sector will be 0),\n"); + printf(" requires a filename given with -f.\n"); + printf("-B numblocks Write the given value as file size in blocks to the directory for\n"); + printf(" the next file. Not applicable for Transwarp files.\n"); + printf("-M numchars Hash computation maximum filename length, this must\n"); + printf(" match loader option FILENAME_MAXLENGTH in Krill's loader.\n"); + printf(" Default is 16.\n"); + printf("-m Ignore filename hash collisions, without this switch a collision\n"); + printf(" results in an error.\n"); + printf("-d track Maintain a shadow directory (copy of the actual directory without\n"); + printf(" a valid BAM).\n"); + printf("-t Use directory track to also store files (makes -x useless)\n"); + printf(" (default no).\n"); + printf("-u numblocks When using -t, amount of dir blocks to leave free (default=2).\n"); + printf("-x Don't split files over directory track hole (default split files).\n"); + printf("-F value Next file first sector on a new track (default=0).\n"); + printf(" Any negative value assumes aligned tracks and uses current\n"); + printf(" sector + interleave - value. After each file, the value falls\n"); + printf(" back to the default. Not applicable for D81.\n"); + printf("-S value Default sector interleave, default=10. Not applicable for D81.\n"); + printf("-s value Next file sector interleave, valid after each file.\n"); + printf(" The interleave value falls back to the default value set by -S\n"); + printf(" after the first sector of the next file. Not applicable for D81.\n"); + printf("-e Start next file on an empty track (default start sector is\n"); + printf(" current sector plus interleave).\n"); + printf("-E Try to fit file on a single track.\n"); + printf("-r track Restrict next file blocks to the specified track or higher.\n"); + printf("-b sector Set next file beginning sector to the specified value.\n"); + printf(" Not applicable for D81.\n"); + printf("-c Save next file cluster-optimized (d71 only).\n"); + printf("-4 Use tracks 35-40 with SPEED DOS BAM formatting.\n"); + printf("-5 Use tracks 35-40 with DOLPHIN DOS BAM formatting.\n"); + printf("-R level Try to restore deleted and formatted files.\n"); + printf(" level 0: Only restore dir entries without touching any t/s links.\n"); + printf(" level 1: Fix dir entries for files with valid t/s chains.\n"); + printf(" level 2: Also add wild sector chains with valid t/s chains.\n"); + printf(" level 3: Also fix dir entries with invalid t/s chains.\n"); + printf(" level 4: Also add and fix wild invalid t/s chains.\n"); + printf(" level 5: Also add reasonable wild single blocks.\n"); + printf("-g filename Write additional g64 output file with given name.\n"); + printf("-a Print command line options that would create the same directory as\n"); + printf(" the one in the given image (for directory art import).\n"); + printf("-U mapping Print PETSCII as Unicode (requires Unicode 13.0 font, e.g.\n"); + printf(" UNSCII). Use mapping 0 for ASCII output, 1 for upper case, 2 for\n"); + printf(" lower case, default is 0.\n"); + printf("-q Be quiet.\n"); + printf("-v Be verbose.\n"); + printf("-h Print this command line help.\n"); + printf("\n"); + + exit(-1); +} + +/* Returns a pointer to the filename in a path */ +static const unsigned char* +basename(const unsigned char* path) +{ + const unsigned char* name; + (name = (unsigned char*)strrchr((char *)path, FILESEPARATOR)) ? ++name : (name = path); + return name; +} + +/* Calculates a hash from a filename to be used by Krill's loader */ +static unsigned int +filenamehash(const unsigned char *filename) +{ + int pos = min(max_hash_length, (int)strlen((char *)filename)); + while ((pos > 0) && (((unsigned char) filename[pos - 1]) == FILENAMEEMPTYCHAR)) { + --pos; + } + + unsigned char hashlo = pos; + unsigned char hashhi = pos; + int carry = 1; + + for (int i = pos - 1; i >= 0; --i) { + unsigned int sum = hashlo + filename[i] + carry; + carry = (sum >= 256) ? 1 : 0; + sum &= 0xff; + hashlo = sum; + sum += hashhi + carry; + carry = (sum >= 256) ? 1 : 0; + hashhi = sum; + } + + return (hashhi << 8) | hashlo; +} + +/* Returns the size of an image in bytes */ +static unsigned int +image_size(image_type type) +{ + switch (type) { + case IMAGE_D64: + return D64SIZE; + + case IMAGE_D64_EXTENDED_SPEED_DOS: + /* fall through */ + case IMAGE_D64_EXTENDED_DOLPHIN_DOS: + return D64SIZE_EXTENDED; + + case IMAGE_D71: + return D71SIZE; + + case IMAGE_D81: + return D81SIZE; + + default: + return 0; + } +} + +/* Returns the number of tracks in an image */ +static unsigned int +image_num_tracks(image_type type) +{ + switch (type) { + case IMAGE_D64: + return D64NUMTRACKS; + + case IMAGE_D64_EXTENDED_SPEED_DOS: + /* fall through */ + case IMAGE_D64_EXTENDED_DOLPHIN_DOS: + return D64NUMTRACKS_EXTENDED; + + case IMAGE_D71: + return D71NUMTRACKS; + + case IMAGE_D81: + return D81NUMTRACKS; + + default: + return 0; + } +} + +/* Returns the number of sectors for a given track */ +static int +num_sectors(image_type type, int track) +{ + return (type == IMAGE_D81) ? SECTORSPERTRACK_D81 + : (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) ? sectors_per_track_extended[track - 1] + : sectors_per_track[track - 1]); +} + +/* Returns the number of blocks in an image */ +static unsigned int +image_num_blocks(image_type type) +{ + int num_blocks = 0; + for (unsigned int t = 1; t <= image_num_tracks(type); t++) { + num_blocks += num_sectors(type, t); + } + return num_blocks; +} + +/* Returns the directory track of an image */ +static int +dirtrack(image_type type) +{ + return (type == IMAGE_D81) ? DIRTRACK_D81 : DIRTRACK_D41_D71; +} + +/* Converts an ASCII character to PETSCII */ +static unsigned char +a2p(unsigned char a) +{ + switch (a) { + case '\n': + return 0x0d; + case '_': + return 0xa4; + case 0x7e: + return 0xff; + default: + if ((a >= 0x5b) && (a < 0x5f)) { + return a; + } + if ((a >= 0x60) && (a <= 0x7e)) { + return a ^ 0x20; + } + if ((a >= 'A') && (a <= 'Z')) { + return a | 0x80; + } + return a; + } +} + +/* Converts a PETSCII character to ASCII */ +static unsigned char +p2a(unsigned char p) +{ + switch (p) { + case 0x0a: + case 0x0d: + return '\n'; + case 0x40: + case 0x60: + return p; + case 0xa0: + case 0xe0: + return ' '; + case 0xa4: + case 0xe4: + return '_'; + default: + switch (p & 0xe0) { + case 0x40: + case 0x60: + p ^= 0x20; + break; + case 0xc0: + p ^= 0x80; + break; + } + } + return ((isprint(p) ? p : '.')); +} + +/* Converts an ASCII string to PETSCII filled up with Shift-Space to length */ +static void +ascii2petscii(const unsigned char* ascii, unsigned char* petscii, int len) +{ + int pos = 0; + while (ascii[pos] != '\0' && pos < len) { + petscii[pos] = a2p(ascii[pos]); + ++pos; + } + while (pos < len) { + petscii[pos] = FILENAMEEMPTYCHAR; + ++pos; + } +} + +/* Prints a PETSCII filename as ASCII with escapes */ +static void +print_filename_with_escapes(const unsigned char* petscii, int len) +{ + /* find end of petscii string */ + while(len > 1 && petscii[len-1] == 0xa0) { + len--; + } + for(int ppos = 0; ppos < len; ppos++) { + unsigned char c = petscii[ppos]; + if((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 32) { + switch (c & 0xe0) { + case 0x40: + case 0x60: + putchar(c ^ 0x20); + break; + default: + putchar(c); + } + } else { + printf("#%02x", c); + } + } +} + +/* Determines length of 0xa0 terminated PETSCII string */ +static int +pstrlen(const unsigned char* string) +{ + int len = 0; + while(string[len] != 0xa0 && len < FILENAMEMAXSIZE) { + len++; + } + return len; +} + +/* Writes digit decimal number into PETSCII string */ +#define PPUTNUM_BUF_LEN 17 +static void +pputnum(unsigned char* string, unsigned int num) +{ + char buffer[PPUTNUM_BUF_LEN]; + snprintf(buffer, PPUTNUM_BUF_LEN, "%d", num); + for(unsigned int len = 0; len < strlen(buffer); len++) { + string[len] = a2p(buffer[len]); + } +} + +/* Writes 2 digit decimal number into PETSCII string */ +#define PPUTNUM2_BUF_LEN 3 +static void +pputnum2(unsigned char* string, unsigned int num) +{ + char buffer[PPUTNUM2_BUF_LEN]; + snprintf(buffer, PPUTNUM2_BUF_LEN, "%02d", num); + for(unsigned int len = 0; len < strlen(buffer); len++) { + string[len] = a2p(buffer[len]); + } +} + +/* Writes 4 digit hex number into PETSCII string */ +#define PPUTHEX_BUF_LEN 5 +static void +pputhex(unsigned char* string, unsigned int num) +{ + char buffer[PPUTHEX_BUF_LEN]; + snprintf(buffer, PPUTHEX_BUF_LEN, "%04x", num); + for(int len = 0; len < 4; len++) { + string[len] = a2p(buffer[len]); + } +} + +/* Converts a two digit hex string to an int */ +static unsigned int +hex2int(char hex) +{ + if ((hex < '0' || hex > '9') && (hex < 'a' || hex > 'f')) { + fprintf(stderr, "ERROR: Invalid hex string in filename\n"); + + exit(-1); + } + if (hex <= '9') { + hex -= '0'; + } else { + hex -= 'a' - 10; + } + return (unsigned int)hex; +} + +/* Converts an ASCII string to PETSCII with escape evaluation filled up with emptychar to length */ +static void +evalhexescape(const unsigned char* ascii, unsigned char* petscii, int len, unsigned char emptychar) +{ + int read = 0, write = 0; + + while (ascii[read] != '\0' && write < len) { + if (ascii[read] == '#') { + unsigned int hi = hex2int(ascii[++read]); + unsigned int lo = hex2int(ascii[++read]); + petscii[write] = (unsigned char)(16 * hi + lo); + } else { + petscii[write] = a2p(ascii[read]); + } + read++; + write++; + } + while (write < len) { + petscii[write] = emptychar; + ++write; + } +} + +/* Converts a unicode character to utf8 */ +char* utf8_encode(int c, char* out) +{ + if (c < 0x80) + *out++ =(c & 0xff); + else if (c < 0x800) { + *out++ = (0xC0 | ((c >> 6) & 0x1f)); + *out++ = (0x80 | (c & 0x3f)); + } else if (c < 0x10000) { + *out++ = (0xE0 | ((c >> 12) & 0xf)); + *out++ = (0x80 | ((c >> 6) & 0x3f)); + *out++ = (0x80 | (c & 0x3f)); + } else { + *out++ = (0xF0 | ((c >> 18) & 0x07)); + *out++ = (0x80 | ((c >> 12) & 0x3f)); + *out++ = (0x80 | ((c >> 6) & 0x3f)); + *out++ = (0x80 | (c & 0x3f)); + } + return out; +} + +#ifdef _WIN32 +/* Enables console formatting under Windows if possible */ +static bool +EnableVTMode() +{ + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) { + return false; + } + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) { + return false; + } + dwMode |= 0x0004; /* ENABLE_VIRTUAL_TERMINAL_PROCESSING not defined for older SDKs */ + if (!SetConsoleMode(hOut, dwMode)) { + return false; + } + return true; +} +#endif + +/* Enables reverse printing to console */ +void reverse_print_on() +{ +#ifdef _WIN32 + /* Avoid escape values for inverse printing under Windows if they are not supported by the console */ + if (EnableVTMode()) { + printf("\033[7m"); + } +#else + printf("\033[7m"); +#endif +} + +/* Disables reverse printing to console */ +void reverse_print_off() +{ +#ifdef _WIN32 + /* Avoid escape values for inverse printing under Windows if they are not supported by the console */ + if (EnableVTMode()) { + printf("\033[m"); + } +#else + printf("\033[m"); +#endif +} + +/* Prints a PETSCII character */ +static void +putp(unsigned char petscii, FILE *file) +{ + if (unicode) { + int u; + int reverse = 0; + + if (petscii < 0x20 || (petscii >= 0x80 && petscii <= 0x9f)) { + reverse = 1; + reverse_print_on(); + petscii += 0x40; + } + + if (unicode == 1) { + u = p2u_uppercase_tab[petscii]; + } else { + u = p2u_lowercase_tab[petscii]; + } + +#ifdef _WIN32 + _setmode(_fileno(file), _O_U16TEXT); + putwc(u, file); + _setmode(_fileno(file), _O_TEXT); +#else + { + char temp[5]; + *utf8_encode(u, temp) = 0; /* fancy way to zero-terminate temp after conversion */ + fputs(temp, file); + } +#endif + + if(reverse) { + reverse_print_off(); + } + } else { + putc(p2a(petscii), file); + } +} + +/* Prints a PETSCII string */ +static void +print_petscii(unsigned char *petscii, int len) +{ + for(int i = 0; i < len; i++) { + putp(petscii[i], stdout); + } +} + +/* Prints the given PETSCII filename like the C64 when listing the directory */ +static void +print_dirfilename(unsigned char* pfilename) +{ + int ended = 0; + putc('\"', stdout); + for (int pos = 0; pos < FILENAMEMAXSIZE; pos++) { + unsigned char c = pfilename[pos]; + if (c == FILENAMEEMPTYCHAR) { + if (!ended) { + putc('\"', stdout); + ended = 1; + } else { + putc(' ', stdout); + } + } else { + putp(c, stdout); + } + } + if (!ended) { + putc('\"', stdout); + } else { + putc(' ', stdout); + } +} + +/* Prints the given PETSCII filename */ +static void +print_filename(FILE *file, unsigned char* pfilename) +{ + putc('\"', file); + for (int pos = 0; pos < FILENAMEMAXSIZE; pos++) { + if (pfilename[pos] == FILENAMEEMPTYCHAR) { + break; + } + putp(pfilename[pos], file); + } + putc('\"', file); +} + +/* Calculates the overall sector index from a given track and sector, returns -1 for invalid track/sector */ +static int +linear_sector(image_type type, int track, int sector) +{ + if ((track < 1) || (track > ((type == IMAGE_D81) ? D81NUMTRACKS : ((type == IMAGE_D64) ? D64NUMTRACKS : (type == IMAGE_D71 ? D71NUMTRACKS : D64NUMTRACKS_EXTENDED))))) { + return -1; + } + + int numsectors = num_sectors(type, track); + if ((sector < 0) || (sector >= numsectors)) { + return -1; + } + + int linear_sector = 0; + for (int i = 0; i < track - 1; i++) { + linear_sector += num_sectors(type, i + 1); + } + linear_sector += sector; + + return linear_sector; +} + +/* Returns the image offset of the bam entry for a given track */ +static int +get_bam_offset(image_type type, unsigned int track) +{ + int bam; + unsigned int offset; + + if (type == IMAGE_D81) { + if (track <= 40) { + bam = linear_sector(type, dirtrack(type), 1 /* sector */) * BLOCKSIZE; + offset = bam + (track * 6) + 11; + } else { + bam = linear_sector(type, dirtrack(type), 2 /* sector */) * BLOCKSIZE; + offset = bam + ((track - 40) * 6) + 11; + } + } else if ((type == IMAGE_D71) && (track > D64NUMTRACKS)) { + /* access second side bam */ + bam = linear_sector(type, dirtrack(type) + D64NUMTRACKS, 0) * BLOCKSIZE; + offset = bam + (track - D64NUMTRACKS - 1) * 3; + } else { + if (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) && (track > D64NUMTRACKS)) { + track -= D64NUMTRACKS; + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE + ((type == IMAGE_D64_EXTENDED_SPEED_DOS) ? BAM_OFFSET_SPEED_DOS : BAM_OFFSET_DOLPHIN_DOS); + } else { + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + } + offset = bam + track * 4 + 1; + } + return offset; +} + +/* Checks if a given sector is marked as free in the BAM and also not used by directory */ +static int +is_sector_free(image_type type, const unsigned char* image, int track, int sector, int numdirblocks, int dir_sector_interleave) +{ + int bam; + const unsigned char* bitmap; + + if (sector < 0) { + fprintf(stderr, "ERROR: Illegal sector %d for track %d\n", sector, track); + + exit(-1); + } + + if (type == IMAGE_D81) { + if (track <= 40) { + bam = linear_sector(type, dirtrack(type), 1 /* sector */) * BLOCKSIZE; + bitmap = image + bam + (track * 6) + 11; + } else { + bam = linear_sector(type, dirtrack(type), 2 /* sector */) * BLOCKSIZE; + bitmap = image + bam + ((track - 40) * 6) + 11; + } + } else if ((type == IMAGE_D71) && (track > D64NUMTRACKS)) { + /* access second side bam */ + bam = linear_sector(type, dirtrack(type) + D64NUMTRACKS, 0) * BLOCKSIZE; + bitmap = image + bam + (track - D64NUMTRACKS - 1) * 3; + } else { + if (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) && (track > D64NUMTRACKS)) { + track -= D64NUMTRACKS+1; + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE + ((type == IMAGE_D64_EXTENDED_SPEED_DOS) ? BAM_OFFSET_SPEED_DOS : BAM_OFFSET_DOLPHIN_DOS); + } else { + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + } + bitmap = image + bam + (track * 4) + 1; + } + + int byte = sector >> 3; + int bit = sector & 7; + + int is_not_dir_block = 1; + if ((track == dirtrack(type)) && (numdirblocks > 0)) { + int dirsector = 0; + int s = 2; + for (int i = 0; is_not_dir_block && (i < numdirblocks); i++) { + switch (i) { + case 0: + dirsector = 0; + break; + + case 1: + dirsector = 1; + break; + + default: + dirsector += dir_sector_interleave; + if (dirsector >= num_sectors(type, track)) { + dirsector = s; + s++; + } + break; + } + is_not_dir_block = (sector != dirsector); + } + } + + return is_not_dir_block && ((bitmap[byte] & (1 << bit)) != 0); +} + +/* Marks given sector in BAM */ +static void +mark_sector(image_type type, unsigned char* image, int track, int sector, int free) +{ + if (free != is_sector_free(type, image, track, sector, 0, 0)) { + int bam; + unsigned char* bitmap; + if (type == IMAGE_D81) { + if (track <= 40) { + bam = linear_sector(type, dirtrack(type), 1 /* sector */) * BLOCKSIZE; + bitmap = image + bam + (track * 6) + 11; + } else { + bam = linear_sector(type, dirtrack(type), 2 /* sector */) * BLOCKSIZE; + bitmap = image + bam + ((track - 40) * 6) + 11; + } + + /* update number of free sectors on track */ + if (free) { + ++bitmap[-1]; + } else { + --bitmap[-1]; + } + } else if ((type == IMAGE_D71) && (track > D64NUMTRACKS)) { + /* access second side bam */ + bam = linear_sector(type, dirtrack(type) + D64NUMTRACKS, 0) * BLOCKSIZE; + bitmap = image + bam + (track - D64NUMTRACKS - 1) * 3; + + /* update number of free sectors on track */ + if (free) { + image[bam + 0xdd + track - D64NUMTRACKS - 1]++; + } else { + image[bam + 0xdd + track - D64NUMTRACKS - 1]--; + } + } else { + if (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) && (track > D64NUMTRACKS)) { + track -= D64NUMTRACKS+1; + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE + ((type == IMAGE_D64_EXTENDED_SPEED_DOS) ? BAM_OFFSET_SPEED_DOS : BAM_OFFSET_DOLPHIN_DOS); + } else { + bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + } + bitmap = image + bam + (track * 4) + 1; + + /* update number of free sectors on track */ + if (free) { + ++image[bam + (track * 4)]; + } else { + --image[bam + (track * 4)]; + } + } + + /* update bitmap */ + int byte = sector >> 3; + int bit = sector & 7; + + if (free) { + bitmap[byte] |= 1 << bit; + } else { + bitmap[byte] &= ~(1 << bit); + } + } +} + +/* Returns offset for header on directory track */ +static int +get_header_offset(image_type type) +{ + int offset; + + if (type == IMAGE_D81) { + offset = 4; + } else { + offset = 0x90; + } + return offset; +} + +/* Returns offset for id on directory track */ +static int +get_id_offset(image_type type) +{ + int offset; + + if (type == IMAGE_D81) { + offset = 0x16; + } else { + offset = 0xa2; + } + return offset; +} + +/* Updates the directory with the given header, id and BAM message */ +static void +update_directory(image_type type, unsigned char* image, unsigned char* header, unsigned char* id, unsigned char *bam_message, int shadowdirtrack) +{ + unsigned int bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + + if (type != IMAGE_D81) { + image[bam + 0x03] = (type == IMAGE_D71) ? 0x80 : 0x00; + } + + /* Set header and ID */ + unsigned char pheader[FILENAMEMAXSIZE]; + unsigned char pid[5]; + evalhexescape(header, pheader, FILENAMEMAXSIZE, FILENAMEEMPTYCHAR); + evalhexescape(id, pid, 5, FILENAMEEMPTYCHAR); + memcpy(image + bam + get_header_offset(type), pheader, FILENAMEMAXSIZE); + memcpy(image + bam + get_id_offset(type), pid, 5); + + /* Set BAM message */ + if(bam_message != NULL) { + unsigned char pbam_message[BAMMESSAGEMAXSIZE]; + int bam_message_len = BAMMESSAGEMAXSIZE; + if(type == IMAGE_D64_EXTENDED_SPEED_DOS) { + bam_message_len = 0xbf - BAMMESSAGEOFFSET; /* avoid conflict with extended BAM and allow for a 0 at the end*/ + } + evalhexescape(bam_message, pbam_message, bam_message_len, 0); + memcpy(image + bam + BAMMESSAGEOFFSET, pbam_message, bam_message_len); + } + + if (type == IMAGE_D81) { + unsigned int bam = linear_sector(type, dirtrack(type), 1 /* sector */) * BLOCKSIZE; + image[bam + 0x04] = id[0]; + image[bam + 0x05] = id[1]; + + bam = linear_sector(type, dirtrack(type), 2 /* sector */) * BLOCKSIZE; + image[bam + 0x04] = id[0]; + image[bam + 0x05] = id[1]; + } + + if (shadowdirtrack > 0) { + unsigned int shadowbam = linear_sector(type, shadowdirtrack, 0 /* sector */) * BLOCKSIZE; + memcpy(image + shadowbam, image + bam, BLOCKSIZE); + + image[shadowbam + 0x00] = shadowdirtrack; + } +} + +/* Writes an empty directory and BAM */ +static void +initialize_directory(image_type type, unsigned char* image, unsigned char* header, unsigned char* id, unsigned char * bam_message, int shadowdirtrack) +{ + unsigned int dir = linear_sector(type, dirtrack(type), 0 /* sector */) * BLOCKSIZE; + + /* Clear image */ + memset(image, 0, image_size(type)); + + /* Write initial BAM */ + if (type == IMAGE_D81) { + image[dir + 0x00] = dirtrack(type); + image[dir + 0x01] = 3; + image[dir + 0x02] = 0x44; + + image[dir + 0x14] = FILENAMEEMPTYCHAR; + image[dir + 0x15] = FILENAMEEMPTYCHAR; + + image[dir + 0x1b] = FILENAMEEMPTYCHAR; + image[dir + 0x1c] = FILENAMEEMPTYCHAR; + + unsigned int bam = linear_sector(type, dirtrack(type), 1 /* sector */) * BLOCKSIZE; + image[bam + 0x00] = dirtrack(type); + image[bam + 0x01] = 2; + image[bam + 0x02] = 0x44; + image[bam + 0x03] = 0xbb; + image[bam + 0x06] = 0xc0; + + bam = linear_sector(type, dirtrack(type), 2 /* sector */) * BLOCKSIZE; + image[bam + 0x00] = 0; + image[bam + 0x01] = 255; + image[bam + 0x02] = 0x44; + image[bam + 0x03] = 0xbb; + image[bam + 0x06] = 0xc0; + } else { + image[dir + 0x00] = dirtrack(type); + image[dir + 0x01] = 1; + image[dir + 0x02] = 0x41; + image[dir + 0x03] = (type == IMAGE_D71) ? 0x80 : 0x00; + + image[dir + 0xa0] = FILENAMEEMPTYCHAR; + image[dir + 0xa1] = FILENAMEEMPTYCHAR; + + image[dir + 0xa7] = FILENAMEEMPTYCHAR; + image[dir + 0xa8] = FILENAMEEMPTYCHAR; + image[dir + 0xa9] = FILENAMEEMPTYCHAR; + image[dir + 0xaa] = FILENAMEEMPTYCHAR; + } + + /* Mark all sectors unused */ + for (unsigned int t = 1; t <= image_num_tracks(type); t++) { + for (int s = 0; s < num_sectors(type, t); s++) { + mark_sector(type, image, t, s, 1 /* free */); + } + } + + /* Reserve space for BAM */ + mark_sector(type, image, dirtrack(type), 0 /* sector */, 0 /* not free */); + if (type == IMAGE_D71) { + mark_sector(type, image, dirtrack(type) + D64NUMTRACKS, 0 /* sector */, 0 /* not free */); + } else if (type == IMAGE_D81) { + mark_sector(type, image, dirtrack(type), 1 /* sector */, 0 /* not free */); + mark_sector(type, image, dirtrack(type), 2 /* sector */, 0 /* not free */); + } + + /* first dir block */ + unsigned int dirblock = linear_sector(type, dirtrack(type), (type == IMAGE_D81) ? 3 : 1) * BLOCKSIZE; + image[dirblock + SECTORLINKOFFSET] = 255; + mark_sector(type, image, dirtrack(type), (type == IMAGE_D81) ? 3 : 1 /* sector */, 0 /* not free */); + + if (shadowdirtrack > 0) { + dirblock = linear_sector(type, shadowdirtrack, (type == IMAGE_D81) ? 3 : 1 /* sector */) * BLOCKSIZE; + image[dirblock + SECTORLINKOFFSET] = 255; + + mark_sector(type, image, shadowdirtrack, 0 /* sector */, 0 /* not free */); + mark_sector(type, image, shadowdirtrack, (type == IMAGE_D81) ? 3 : 1 /* sector */, 0 /* not free */); + } + + update_directory(type, image, header, id, bam_message, shadowdirtrack); +} + +/* Computes Transwarp dirdata checksum */ +static unsigned char +transwarp_dirdata_checksum(const unsigned char *image, int dir_entry_offset) +{ + int dirdata_checksum = 0; + int carry = 1; + for (int offset = DIRDATACHECKSUMOFFSET; offset <= FILEBLOCKSLOOFFSET; ++offset) { + dirdata_checksum += (image[dir_entry_offset + offset] + carry); + carry = (dirdata_checksum >= 0x0100) ? 1 : 0; + dirdata_checksum &= 0xff; + } + + return dirdata_checksum; +} + +/* Checks if a given dir entry points to a Transwarp file */ +static bool +is_transwarp_file(const unsigned char *image, int dir_entry_offset) +{ + return (image[dir_entry_offset + TRANSWARPSIGNATROFFSLO] == TRANSWARPSIGNATURELO) + && (image[dir_entry_offset + TRANSWARPSIGNATROFFSHI] == TRANSWARPSIGNATUREHI) + && (transwarp_dirdata_checksum(image, dir_entry_offset) == 0); +} + +/* Checks if a given dir entry points to the Transwarp bootfile */ +static bool +is_transwarp_bootfile(const unsigned char *image, int dir_entry_offset) +{ + return memcmp(image + dir_entry_offset + FILENAMEOFFSET, TRANSWARP, strlen(TRANSWARP)) == 0; +} + +/* Return Transwarp file stat */ +static int +transwarp_stat(image_type type, const unsigned char *image, int dir_entry_offset, int *start_track, int *end_track, int *low_track, int *high_track) +{ + *start_track = 0; + *end_track = 0; + *low_track = 0; + *high_track = 0; + + int filesize = (image[dir_entry_offset + ENDADDRESSLOOFFSET] | (image[dir_entry_offset + ENDADDRESSHIOFFSET] << 8)) + - (image[dir_entry_offset + LOADADDRESSLOOFFSET] | (image[dir_entry_offset + LOADADDRESSHIOFFSET] << 8)); + if (filesize <= 0) { + return 0; + } + + *start_track = image[dir_entry_offset + TRANSWARPTRACKOFFSET]; + *end_track = *start_track; + + int size = filesize; + + while (filesize > 0) { + filesize -= (TRANSWARPBLOCKSIZE * num_sectors(type, *end_track)); + if (filesize > 0) { + *end_track = (*end_track < DIRTRACK_D41_D71) ? (*end_track - 1) : (*end_track + 1); + } + } + + *low_track = (*start_track < *end_track) ? *start_track : *end_track; + *high_track = (*start_track > *end_track) ? *start_track : *end_track; + + return size; +} + +/* Return Transwarp file stat */ +static int +transwarp_size(image_type type, int start_track, int end_track, int filesize, + int *transwarp_blocks, int *nonredundant_blocks, int *redundant_blocks, int *nonredundant_blocks_on_last_track) +{ + *transwarp_blocks = 0; + + int last_track_sectors; + if (start_track <= end_track) { + for (int track = start_track; track <= end_track; ++track) { + last_track_sectors = num_sectors(type, track); + *transwarp_blocks += last_track_sectors; + } + } else { + for (int track = start_track; track >= end_track; --track) { + last_track_sectors = num_sectors(type, track); + *transwarp_blocks += last_track_sectors; + } + } + + int spare_bytes = TRANSWARPBLOCKSIZE - (filesize % TRANSWARPBLOCKSIZE); + if (spare_bytes == TRANSWARPBLOCKSIZE) { + spare_bytes = 0; + } + + *nonredundant_blocks = (filesize / TRANSWARPBLOCKSIZE) + ((spare_bytes == 0) ? 0 : 1); + *redundant_blocks = *transwarp_blocks - *nonredundant_blocks; + *nonredundant_blocks_on_last_track = last_track_sectors - *redundant_blocks; + + return spare_bytes; +} + +/* Deletes a file from disk and BAM, but leaves the directory entry */ +static void +wipe_file(image_type type, unsigned char* image, imagefile* file) +{ + int b = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + + if (is_transwarp_file(image, b)) { + int start_track; + int end_track; + int low_track; + int high_track; + int filesize = transwarp_stat(type, image, b, &start_track, &end_track, &low_track, &high_track); + if (filesize <= 0) { + fprintf(stderr, "ERROR: Cannot overwrite Transwarp file "); + print_filename(stderr, file->pfilename); + fprintf(stderr, "\n"); + + exit(-1); + } + + for (int track = low_track; track <= high_track; ++track) { + for (int sector = 0; sector < num_sectors(type, track); ++sector) { + int block_offset = linear_sector(type, track, sector) * BLOCKSIZE; + memset(image + block_offset, 0, BLOCKSIZE); + mark_sector(type, image, track, sector, 1 /* free */); + } + } + + return; + } + + unsigned int track = image[b + FILETRACKOFFSET]; + unsigned int sector = image[b + FILESECTOROFFSET]; + + if (sector >= 0x80) { + return; /* loop file */ + } + + while (track != 0) { + int block_offset = linear_sector(type, track, sector) * BLOCKSIZE; + int next_track = image[block_offset + TRACKLINKOFFSET]; + int next_sector = image[block_offset + SECTORLINKOFFSET]; + memset(image + block_offset, 0, BLOCKSIZE); /* this also fixes any cyclic t/s chain */ + mark_sector(type, image, track, sector, 1 /* free */); + track = next_track; + sector = next_sector; + } +} + +/* Sets image offset to the next DIR entry, returns false when the DIR end was reached */ +static bool +next_dir_entry(image_type type, const unsigned char* image, int *track, int *sector, int *offset, char *blockmap) +{ + int b = linear_sector(type, *track, *sector); /* assuming here that the given t/s is valid */ + blockmap[b] = 1; + if (*offset % BLOCKSIZE == 7 * DIRENTRYSIZE) { + /* last entry in sector */ + int next_track = image[b * BLOCKSIZE + TRACKLINKOFFSET]; + if (next_track == 0) { + /* this was the last DIR sector */ + return false; + } + int next_sector = image[b * BLOCKSIZE + SECTORLINKOFFSET]; + b = linear_sector(type, next_track, next_sector); + if(b < 0) { + dir_error = DIR_ILLEGAL_TS; + return false; + } + if(blockmap[b]) { + dir_error = DIR_CYCLIC_TS; + return false; + } + *track = next_track; + *sector = next_sector; + *offset = 0; + } else { + *offset += DIRENTRYSIZE; + } + return true; +} + +/* Finds all filenames with the given hash */ +static int +count_hashes(image_type type, const unsigned char* image, unsigned int hash, bool print) +{ + int num = 0; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET]; + + if (filetype != FILETYPEDEL) { + unsigned char *filename = (unsigned char *) image + dirblock + FILENAMEOFFSET; + if (hash == filenamehash(filename)) { + ++num; + + if (print) { + printf(" [$%04x] ", filenamehash(filename)); + print_filename(stdout, filename); + printf("\n"); + } + } + } + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); + + return num; +} + +/* Checks if multiple filenames have the same hash */ +static bool +check_hashes(image_type type, const unsigned char* image) +{ + bool collision = false; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + printf("\n"); + + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET]; + + if (filetype != FILETYPEDEL) { + unsigned char *filename = (unsigned char *) image + dirblock + FILENAMEOFFSET; + if (count_hashes(type, image, filenamehash(filename), false /* print */) > 1) { + collision = 1; + fprintf(stderr, "Hash of filename "); + print_filename(stderr, filename); + fprintf(stderr, " [$%04x] is not unique\n", filenamehash(filename)); + count_hashes(type, image, filenamehash(filename), true /* print */); + } + } + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); + return collision; +} + +/* Searches for an existing DIR entry with the given name, returns false if it does not exist */ +static bool +find_existing_file(image_type type, unsigned char* image, unsigned char* filename, int *index, int *track, int *sector, int *offset) +{ + *track = dirtrack(type); + *sector = (type == IMAGE_D81) ? 3 : 1; + *offset = 0; + *index = 0; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + do { + int b = linear_sector(type, *track, *sector) * BLOCKSIZE + *offset; + int filetype = image[b + FILETYPEOFFSET]; + if(filetype != 0 && memcmp(image + b + FILENAMEOFFSET, filename, FILENAMEMAXSIZE) == 0) { + return true; + } + ++(*index); + } while (next_dir_entry(type, image, track, sector, offset, blockmap)); + + free(blockmap); + return false; +} + +/* Returns an empty DIR slot, allocates a new DIR sector if required */ +static void +new_dir_slot(image_type type, unsigned char* image, int dir_sector_interleave, int shadowdirtrack, int *index, int *dirsector, int *entry_offset, imagefile files[]) +{ + int track = dirtrack(type); + *dirsector = (type == IMAGE_D81) ? 3 : 1; + *entry_offset = 0; + int lindex = 0; + + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + do { + int b = linear_sector(type, track, *dirsector) * BLOCKSIZE + *entry_offset; + if (image[b + FILETYPEOFFSET] == FILETYPEDEL) { + /* Check if the apparently free slot is used by a new filetype 0 file */ + bool used = false; + for(int f = 0; f < num_files; f++) { + if(files[f].direntryindex == lindex) { + used = true; + break; + } + } + if(!used) { + *index = lindex; + free(blockmap); + return; /* found an empty slot */ + } + } + ++lindex; + } while (next_dir_entry(type, image, &track, dirsector, entry_offset, blockmap)); + free(blockmap); + + /* allocate new dir block */ + int last_sector = *dirsector; + int next_sector = -1; + for (int s = 1; s < num_sectors(type, dirtrack(type)); s++) { + int sector = (last_sector + s * dir_sector_interleave) % num_sectors(type, dirtrack(type)); + if (is_sector_free(type, image, dirtrack(type), sector, 0, 0)) { + next_sector = sector; + break; + } + } + if (next_sector == -1) { + fprintf(stderr, "ERROR: Dir track full\n"); + exit(-1); + } + int b = linear_sector(type, dirtrack(type), last_sector) * BLOCKSIZE; + image[b + TRACKLINKOFFSET] = dirtrack(type); + image[b + SECTORLINKOFFSET] = next_sector; + + mark_sector(type, image, dirtrack(type), next_sector, 0 /* not free */); + b = linear_sector(type, dirtrack(type), next_sector) * BLOCKSIZE; + memset(image + b, 0, BLOCKSIZE); + image[b + SECTORLINKOFFSET] = 255; + *dirsector = next_sector; + *entry_offset = 0; + + if (shadowdirtrack > 0) { + b = linear_sector(type, shadowdirtrack, last_sector) * BLOCKSIZE; + image[b + TRACKLINKOFFSET] = shadowdirtrack; + image[b + SECTORLINKOFFSET] = next_sector; + mark_sector(type, image, shadowdirtrack, next_sector, 0 /* not free */); + + b = linear_sector(type, shadowdirtrack, next_sector) * BLOCKSIZE; + memset(image + b, 0, BLOCKSIZE); + image[b + SECTORLINKOFFSET] = 255; + } + *index = lindex; +} + +/* Returns suitable index and offset for given filename (either existing slot when overwriting, first free slot or slot in newly allocated segment) */ +static bool +find_dir_slot(image_type type, unsigned char* image, unsigned char* filename, int dir_sector_interleave, int shadowdirtrack, int *index, int *dirsector, int *entry_offset, imagefile files[]) +{ + int track; + if(find_existing_file(type, image, filename, index, &track, dirsector, entry_offset)) { + return true; + } + new_dir_slot(type, image, dir_sector_interleave, shadowdirtrack, index, dirsector, entry_offset, files); + return false; +} + +/* Adds the specified new entries to the directory */ +static void +create_dir_entries(image_type type, unsigned char* image, imagefile* files, int num_files, int dir_sector_interleave, unsigned int shadowdirtrack, int nooverwrite) +{ + /* this does not check for uniqueness of filenames */ + + int num_overwritten_files = 0; + + if (verbose && num_files > 0) { + printf("\nCreating dir entries:\n"); + } + + for (int i = 0; i < num_files; i++) { + /* find or create slot */ + imagefile *file = files + i; + + if (verbose) { + printf(" "); + print_dirfilename(file->pfilename); + } + + if(file->force_new) { + new_dir_slot(type, image, dir_sector_interleave, shadowdirtrack, &file->direntryindex, &file->direntrysector, &file->direntryoffset, files); + } else if (find_dir_slot(type, image, file->pfilename, dir_sector_interleave, shadowdirtrack, &file->direntryindex, &file->direntrysector, &file->direntryoffset, files)) { + if (nooverwrite) { + fprintf(stderr, "ERROR: Filename exists on disk image already and -o was set\n"); + exit(-1); + } + + wipe_file(type, image, file); + num_overwritten_files++; + } + + int file_entry_offset = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[file_entry_offset + FILETYPEOFFSET] = file->filetype & 0xff; + if (verbose && (file->filetype & FILETYPETRANSWARPMASK)) { + printf(" [Transwarp]"); + } + memcpy(image + file_entry_offset + FILENAMEOFFSET, file->pfilename, FILENAMEMAXSIZE); + + if (is_transwarp_bootfile(image, file_entry_offset)) { + if (file->filetype & FILETYPETRANSWARPMASK) { + if (verbose) { + printf("\n"); + } + + fprintf(stderr, "ERROR: Attempt to write Transwarp bootfile as Transwarp file\n"); + + exit(-1); + } + + file->mode |= MODE_TRANSWARPBOOTFILE; + if (verbose) { + printf(" [Transwarp bootfile]"); + } + + if (i != 0) { + // allocate Transwarp bootfile first + if ((files[0].mode & MODE_TRANSWARPBOOTFILE) != 0) { + if (verbose) { + printf("\n"); + } + + fprintf(stderr, "ERROR: Multiple Transwarp bootfiles\n"); + exit(-1); + } + + imagefile transwarp_bootfile = *file; + for (int j = i; j > 0; --j) { + files[j] = files[j - 1]; + } + files[0] = transwarp_bootfile; + file = files; + } + } + + if (shadowdirtrack > 0) { + file_entry_offset = linear_sector(type, shadowdirtrack, file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[file_entry_offset + FILETYPEOFFSET] = file->filetype; + memcpy(image + file_entry_offset + FILENAMEOFFSET, file->pfilename, FILENAMEMAXSIZE); + } + + if (verbose) { + printf("\n"); + } + } + + if (!quiet && (num_overwritten_files > 0)) { + printf("%d out of %d files exist and will be overwritten\n", num_overwritten_files, num_files); + } +} + +/* Prints the allocated tracks and sectors for every file */ +static void +print_file_allocation(image_type type, const unsigned char* image, imagefile* files, int num_files) +{ + if (num_files <= 0) { + imagefile existing_files[144]; + memset(existing_files, 0, sizeof existing_files); + + int t = dirtrack(type); + int s = (type == IMAGE_D81) ? 3 : 1; + int o = 0; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + do { + int b = linear_sector(type, t, s) * BLOCKSIZE + o; + int filetype = image[b + FILETYPEOFFSET] & 0xf; + switch (filetype) { + case FILETYPEPRG: { + int track = image[b + FILETRACKOFFSET]; + int sector = image[b + FILESECTOROFFSET]; + const unsigned char *filename = image + b + FILENAMEOFFSET; + b = linear_sector(type, track, sector); + if (b >= 0) { + memcpy(existing_files[num_files].pfilename, filename, FILENAMEMAXSIZE); + existing_files[num_files].track = track; + existing_files[num_files].sector = sector; + existing_files[num_files].direntryindex = num_files; + existing_files[num_files].direntrysector = s; + existing_files[num_files].direntryoffset = o; + existing_files[num_files].nrSectors = image[b + FILEBLOCKSLOOFFSET] + 256 * image[b + FILEBLOCKSHIOFFSET]; + + if (is_transwarp_file(image, b)) { + existing_files[num_files].filetype = filetype | FILETYPETRANSWARPMASK; + existing_files[num_files].sectorInterleave = 1; + + int start_track; + int end_track; + int low_track; + int high_track; + int filesize = transwarp_stat(type, image, b, &start_track, &end_track, &low_track, &high_track); + + existing_files[num_files].size = filesize; + + existing_files[num_files].track = start_track; + existing_files[num_files].last_track = end_track; + + ++num_files; + + break; + } + + while (true) { + b = linear_sector(type, track, sector); + if(b < 0) { + break; // TODO: print info about illegal t/s + } + int offset = b * BLOCKSIZE; + int next_track = image[offset + 0]; + int next_sector = image[offset + 1]; + if ((track == 0) || (next_track == 0)) { + break; + } + if ((track == next_track) && (next_sector > sector)) { + existing_files[num_files].sectorInterleave = next_sector - sector; + break; + } + track = next_track; + sector = next_sector; + } + + ++num_files; + + break; + } + } + + default: + break; + } + } while (next_dir_entry(type, image, &t, &s, &o, blockmap)); + free(blockmap); + files = existing_files; + } + + if(num_files > 0) { + printf("\nFile allocation:\n"); + } + + for (int i = 0; i < num_files; i++) { + printf("%02d/%02d %3d (0x%02x 0x%02x:0x%02x) ", files[i].track, files[i].sector, files[i].nrSectors & 0xffff, + files[i].direntryindex, files[i].direntrysector, files[i].direntryoffset); + if (files[i].alocalname) { + printf("\"%s\" => ", files[i].alocalname); + } + print_filename(stdout, files[i].pfilename); + printf(" (SL: %d)", files[i].sectorInterleave); + + if ((files[i].mode & MODE_LOOPFILE) && (files[i].sectorInterleave != 0)) { + + continue; + } + + if (files[i].filetype & FILETYPETRANSWARPMASK) { + int transwarp_blocks; + int nonredundant_blocks; + int redundant_blocks; + int nonredundant_blocks_on_last_track; + int spare_bytes = transwarp_size(type, files[i].track, files[i].last_track, files[i].size, &transwarp_blocks, &nonredundant_blocks, &redundant_blocks, &nonredundant_blocks_on_last_track); + + int num_blocks = 0; + int filesize = files[i].size + 2; + while (filesize > 0) { + ++num_blocks; + filesize -= 254; + } + + printf("\n Transwarp: %d total/%d actual (%d standard) blocks, tracks %d-%d, %d used and %d redundant block%s on last track, 0x%x spare bytes in last block, size 0x%x\n", + transwarp_blocks, nonredundant_blocks, num_blocks, + (files[i].track < files[i].last_track) ? files[i].track : files[i].last_track, + (files[i].track >= files[i].last_track) ? files[i].track : files[i].last_track, + nonredundant_blocks_on_last_track, redundant_blocks, (redundant_blocks == 1) ? "" : "s", + spare_bytes, files[i].size); + + continue; + } + + int track = files[i].track; + int sector = files[i].sector; + + bool firsttrack = true; + int firstsector = sector; + bool fileblocks[SECTORSPERTRACK_D81]; + memset(fileblocks, 0, sizeof fileblocks); + fileblocks[sector] = true; + + int j = 0; + while (track != 0) { + if (j == 0) { + printf("\n "); + } + printf("%02d/%02d", track, sector); + int b = linear_sector(type, track, sector); + if(b < 0) { + break; // TODO: print info about illegal t/s link? + } + int offset = b * BLOCKSIZE; + int next_track = image[offset + 0]; + int next_sector = image[offset + 1]; + if ((track != next_track) && (next_track != 0)) { + /* track change */ + if (next_sector != 0) { + /* interleave violation */ + printf("!-"); + } else { + printf(" -"); + } + } else if ((next_sector < sector) && (next_track != 0)) { + /* sector wrap */ + int expected_next_sector = ((sector + abs(files[i].sectorInterleave)) % num_sectors(type, track)); + bool on_nonempty_firsttrack = (expected_next_sector < next_sector) && firsttrack && (firstsector != 0); + if ((expected_next_sector != next_sector) && (!on_nonempty_firsttrack)) { + while ((expected_next_sector < next_sector) && fileblocks[expected_next_sector]) { + ++expected_next_sector; + } + if (expected_next_sector != next_sector) { + /* interleave violation */ + printf("!."); + } else { + printf(" ."); + } + } else { + printf(" ."); + } + } else if (((next_sector - sector) != abs(files[i].sectorInterleave)) && (next_track != 0)) { + /* interleave violation */ + printf(" !"); + } else { + printf(" "); + } + + if (track != next_track) { + memset(fileblocks, 0, sizeof fileblocks); + firsttrack = false; + } + track = next_track; + sector = next_sector; + if (next_track != 0) { + fileblocks[sector] = true; + } + + j++; + if (j == 10) { + j = 0; + } + } + printf("\n"); + } + printf("\n"); +} + + +static void +assign_blocktags(image_type type, const unsigned char *image, int(*blocktags)[SECTORSPERTRACK_D81]) +{ + char c = '@'; + + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET] & 0xf; + if (filetype != 0) { + int filetrack = image[dirblock + FILETRACKOFFSET]; + int filesector = image[dirblock + FILESECTOROFFSET]; + + if (is_transwarp_file(image, dirblock)) { + int start_track; + int end_track; + int low_track; + int high_track; + int filesize = transwarp_stat(type, image, dirblock, &start_track, &end_track, &low_track, &high_track); + if (filesize <= 0) { + continue; + } + + int transwarp_blocks; + int nonredundant_blocks; + int redundant_blocks; + int nonredundant_blocks_on_last_track; + transwarp_size(type, start_track, end_track, filesize, &transwarp_blocks, &nonredundant_blocks, &redundant_blocks, &nonredundant_blocks_on_last_track); + + for (int track = low_track; track <= high_track; ++track) { + for (int sector = 0; sector < SECTORSPERTRACK_D81; ++sector) { + blocktags[track][sector] = c + (((track == end_track) && (sector >= nonredundant_blocks_on_last_track)) ? 256 : 0); + } + } + } else { + bool new_track = true; + while (filetrack != 0) { + int b = linear_sector(type, filetrack, filesector); + if(b < 0) { + break; + } + int block_offset = b * BLOCKSIZE; + int next_track = image[block_offset + TRACKLINKOFFSET]; + int next_sector = image[block_offset + SECTORLINKOFFSET]; + + blocktags[filetrack][filesector] = c + (new_track ? 256 : 0); + new_track = (filetrack != next_track); + + filetrack = next_track; + filesector = next_sector; + } + } + + switch (c) { + default: + ++c; + break; + case 'Z': + c = '0'; + break; + case '9': + c = 'a'; + break; + case 'z': + c = '@'; + break; + } + } + + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); +} + +/* Returns true if the file starting on the given filetrack and filesector uses the given track */ +static bool +fileontrack(image_type type, const unsigned char *image, int track, int filetrack, int filesector) +{ + while (filetrack != 0) { + if (filetrack == track) { + return true; + } + + int b = linear_sector(type, filetrack, filesector); + if(b < 0) { + return false; + } + int block_offset = b * BLOCKSIZE; + int next_track = image[block_offset + TRACKLINKOFFSET]; + int next_sector = image[block_offset + SECTORLINKOFFSET]; + filetrack = next_track; + filesector = next_sector; + } + + return false; +} + +/* Prints all filenames of files that use the given track */ +static void +print_track_usage(image_type type, const unsigned char *image, int(*blocktags)[SECTORSPERTRACK_D81], int track) +{ + + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET] & 0xf; + if (filetype != 0) { + int filetrack = image[dirblock + FILETRACKOFFSET]; + int filesector = image[dirblock + FILESECTOROFFSET]; + bool ontrack = (type == IMAGE_D71) ? fileontrack(type, image, track, (filetrack > D64NUMTRACKS) ? filetrack - D64NUMTRACKS : filetrack + D64NUMTRACKS, filesector) : false; + + if (is_transwarp_file(image, dirblock)) { + int start_track; + int end_track; + int low_track; + int high_track; + int filesize = transwarp_stat(type, image, dirblock, &start_track, &end_track, &low_track, &high_track); + if (filesize <= 0) { + continue; + } + + ontrack = (low_track <= track) && (track <= high_track); + if (ontrack == false) { + continue; + } + + filetrack = start_track; + } + + if (ontrack || fileontrack(type, image, track, filetrack, filesector)) { + unsigned char *filename = (unsigned char *) image + dirblock + FILENAMEOFFSET; + if (track == filetrack) { + reverse_print_on(); + } + printf("%c", blocktags[filetrack][filesector]); + if (track == filetrack) { + reverse_print_off(); + } + printf(": "); + print_filename(stdout, filename); + printf(" "); + } + } + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); +} + +/* Prints the BAM allocation map and returns the number of free blocks */ +static int +check_bam(image_type type, const unsigned char* image) +{ + int sectorsFree = 0; + int sectorsFreeOnDirTrack = 0; + int sectorsOccupied = 0; + int sectorsOccupiedOnDirTrack = 0; + + int blocktags[D81NUMTRACKS][SECTORSPERTRACK_D81]; + + if (verbose) { + memset(blocktags, 0, sizeof blocktags); + assign_blocktags(type, image, blocktags); + + printf("Block allocation:\n"); + } + + int max_track = (type == IMAGE_D81) ? D81NUMTRACKS + : (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) ? D64NUMTRACKS_EXTENDED + : D64NUMTRACKS); + for (int t = 1; t <= max_track; t++) { + + if (verbose) { + printf(" %2d: ", t); + } + for (int s = 0; s < num_sectors(type, t); s++) { + if (is_sector_free(type, image, t, s, 0, 0)) { + if (verbose) { + printf("."); + } + if (t != dirtrack(type)) { + sectorsFree++; + } else { + sectorsFreeOnDirTrack++; + } + } else { + if (verbose) { + int blocktag = blocktags[t][s]; + if (blocktag == 0) { + blocktag = '#'; + } + if (blocktag >= 256) { + reverse_print_on(); + } + printf("%c", blocktag); + if (blocktag >= 256) { + reverse_print_off(); + } + } + if (t != dirtrack(type)) { + sectorsOccupied++; + } else { + sectorsOccupiedOnDirTrack++; + } + } + } + + if (type == IMAGE_D71) { + for (int i = num_sectors(type, t); i < 23; i++) { + if (verbose) { + printf(" "); + } + } + int t2 = t + D64NUMTRACKS; + + if (verbose) { + printf("%2d: ", t2); + } + for (int s = 0; s < num_sectors(type, t2); s++) { + if (is_sector_free(type, image, t2, s, 0, 0)) { + if (verbose) { + printf("."); + } + if (t2 != dirtrack(type)) { + sectorsFree++; + } else { + /* track 53 is usually empty except the extra BAM block */ + sectorsFreeOnDirTrack++; + } + } else { + if (verbose) { + printf("#"); + } + sectorsOccupied++; + } + } + } + + for (int i = ((type == IMAGE_D81) ? 42 : 23) - num_sectors(type, t); i > 0; --i) { + if (verbose) { + printf(" "); + } + } + if (verbose) { + print_track_usage(type, image, blocktags, t); + printf("\n"); + } + } + if (verbose) { + printf("%3d/%3d blocks free (%d/%d including dir track)\n", sectorsFree, sectorsFree + sectorsOccupied, + sectorsFree + sectorsFreeOnDirTrack, sectorsFree + sectorsFreeOnDirTrack + sectorsOccupied + sectorsOccupiedOnDirTrack); + } + + return sectorsFree; +} + +/* Prints the filetype like the C64 when listing the directory */ +static void +print_filetype(int filetype) +{ + if ((filetype & 0x80) == 0) { + printf("*"); + } else { + printf(" "); + } + if(unicode == 1) { + printf("%s", filetypename_uc[filetype & 0xf]); + } else { + printf("%s", filetypename_lc[filetype & 0xf]); + } + if ((filetype & 0x40) != 0) { + printf("<"); + } else { + printf(" "); + } +} + +/* Prints the directory like the C64 when listing the directory */ +static void +print_directory(image_type type, unsigned char* image, int blocks_free) +{ + unsigned char* bam = image + linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + printf("\n0 "); + reverse_print_on(); + printf("\""); + print_petscii(bam + get_header_offset(type), 16); + printf("\" "); + print_petscii(bam + get_id_offset(type), 5); + reverse_print_off(); + + if (verbose) { + printf(" fn hash"); + } + printf("\n"); + + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET]; + int blocks = image[dirblock + FILEBLOCKSLOOFFSET] + 256 * image[dirblock + FILEBLOCKSHIOFFSET]; + + if (filetype != FILETYPEDEL) { + unsigned char* filename = (unsigned char*)image + dirblock + FILENAMEOFFSET; + printf("%-3d ", blocks); + print_dirfilename(filename); + print_filetype(filetype); + if (verbose) { + printf(" [$%04x]", filenamehash(filename)); + } + printf("\n"); + } + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); + if(unicode == 1) { + printf("%d BLOCKS FREE.\n", blocks_free); + } else { + printf("%d blocks free.\n", blocks_free); + } + + /* detect and print bam message */ + if((type == IMAGE_D64 || type == IMAGE_D64_EXTENDED_SPEED_DOS) && bam[BAMMESSAGEOFFSET] != 0) { + printf("\nBAM message: \""); + /* only checking the first byte and hoping for the best... */ + int b = BAMMESSAGEOFFSET; + unsigned char c; + while(b < 256 && ((c = bam[b]) != 0)) { + putp(c, stdout); + b++; + } + printf("\"\n"); + } +} + +/* Performs GCR encoding on 32 bit value */ + +static const unsigned char NIBBLE_TO_GCR[] = { + 0x0a, 0x0b, 0x12, 0x13, + 0x0e, 0x0f, 0x16, 0x17, + 0x09, 0x19, 0x1a, 0x1b, + 0x0d, 0x1d, 0x1e, 0x15 +}; + +static void +encode_4_bytes_gcr(char* in, char* out) +{ + out[0] = (NIBBLE_TO_GCR[(in[0] >> 4) & 0xf] << 3) | (NIBBLE_TO_GCR[ in[0] & 0xf] >> 2); /* 11111222 */ + out[1] = (NIBBLE_TO_GCR[ in[0] & 0xf] << 6) | (NIBBLE_TO_GCR[(in[1] >> 4) & 0xf] << 1) | (NIBBLE_TO_GCR[ in[1] & 0xf] >> 4); /* 22333334 */ + out[2] = (NIBBLE_TO_GCR[ in[1] & 0xf] << 4) | (NIBBLE_TO_GCR[(in[2] >> 4) & 0xf] >> 1); /* 44445555 */ + out[3] = (NIBBLE_TO_GCR[(in[2] >> 4) & 0xf] << 7) | (NIBBLE_TO_GCR[ in[2] & 0xf] << 2) | (NIBBLE_TO_GCR[(in[3] >> 4) & 0xf] >> 3); /* 56666677 */ + out[4] = (NIBBLE_TO_GCR[(in[3] >> 4) & 0xf] << 5) | NIBBLE_TO_GCR[ in[3] & 0xf]; /* 77788888 */ +} + +/* Transwarp encoding utility functions */ + +static void +generate_gcr_decoding_table(const unsigned char nibble_to_gcr[], int8_t gcr_to_nibble[]) +{ + for (int i = 0; i < 32; ++i) { + gcr_to_nibble[i] = -(i + 1); + } + + for (int i = 0; i < 16; ++i) { + gcr_to_nibble[nibble_to_gcr[i]] = i; + } +} + +static unsigned char +even_bits(unsigned char value) +{ + return (((value >> 6) & 1) << 3) + | (((value >> 4) & 1) << 2) + | (((value >> 2) & 1) << 1) + | (((value >> 0) & 1) << 0); +} + +static unsigned char +odd_bits(unsigned char value) +{ + return (((value >> 7) & 1) << 3) + | (((value >> 5) & 1) << 2) + | (((value >> 3) & 1) << 1) + | (((value >> 1) & 1) << 0); +} + +typedef struct transwarp_encode_context { + unsigned int version; + unsigned char previous; + unsigned char previous1; + unsigned char previous2; + unsigned char accu; + unsigned char carry; + unsigned char recvcarry; + unsigned char carry2; + unsigned char sendaccu; + unsigned char sendcarry; +} transwarp_encode_context; + +static const int ENCODE[5][64] = { + { + 0xf6, 0xee, 0xf5, 0xed, 0x9a, 0xde, 0x96, 0xda, 0xf3, 0xea, 0xf2, 0x9e, 0x93, 0xd6, 0x92, 0xd3, + 0xd2, 0xca, 0xce, 0xbe, 0xb3, 0x7e, 0xb2, 0x7d, 0xcd, 0xba, 0xcb, 0xb6, 0xae, 0x7b, 0xaa, 0x7a, + 0x76, 0x6e, 0x75, 0x6d, 0x5e, 0x5b, 0x5d, 0x5a, 0x73, 0x6b, 0x72, 0x6a, 0xdb, 0x9d, 0xeb, 0xd5, + 0x56, 0x4e, 0x55, 0x4d, 0xbd, 0xb5, 0xbb, 0xad, 0x53, 0x4b, 0x52, 0xdd, 0xab, 0x4a, 0x9b, 0x95 + }, { + 0xf6, 0xee, 0xf5, 0xed, 0x9a, 0xde, 0x96, 0xda, 0xf3, 0xea, 0xf2, 0x9e, 0x93, 0xd6, 0x92, 0xef, + 0xe7, 0x7c, 0x9f, 0x74, 0xe6, 0x6c, 0xdf, 0xa6, 0x9c, 0xba, 0x94, 0xb6, 0xae, 0x7b, 0xaa, 0x7a, + 0x76, 0x6e, 0x75, 0x6d, 0x5e, 0x5b, 0x5d, 0x5a, 0x73, 0x6b, 0x72, 0x6a, 0xdb, 0xd7, 0xeb, 0xe5, + 0x56, 0x64, 0x55, 0x5c, 0xbd, 0xb5, 0xbb, 0xad, 0x65, 0x54, 0x5f, 0xdd, 0xab, 0xa7, 0x9b, 0xa5 + }, { + 0xa5, 0xa7, 0xa9, 0xab, 0xd5, 0xd7, 0xd9, 0xdb, 0x95, 0x99, 0x9b, 0x97, 0xe5, 0x9d, 0xeb, 0xe9 + }, { + 0xf6, 0xee, 0xf5, 0xed, 0x69, 0xde, 0x59, 0xda, 0xb9, 0xea, 0xb7, 0x6f, 0x57, 0xd6, 0x4f, 0xef, + 0xe7, 0xca, 0xce, 0xbe, 0xe6, 0xbf, 0xdf, 0xa6, 0xcd, 0xba, 0xcb, 0xb6, 0xae, 0x7b, 0xaa, 0x7a, + 0x76, 0x6e, 0x75, 0x6d, 0x5e, 0x5b, 0x5d, 0x5a, 0x67, 0x6b, 0x66, 0x6a, 0xe9, 0xd7, 0xeb, 0xe5, + 0x56, 0x4e, 0x55, 0x4d, 0xbd, 0xb5, 0xbb, 0xad, 0x65, 0x4b, 0x5f, 0xdd, 0xab, 0xa7, 0xa9, 0xa5 + }, { + 0xcf, 0xaf, 0xc9, 0x79, 0x69, 0xde, 0x59, 0xda, 0xb9, 0x77, 0xb7, 0x6f, 0x57, 0xd6, 0x4f, 0xd3, + 0xd2, 0xca, 0xce, 0xbe, 0xb3, 0x7e, 0xb2, 0x7d, 0xcd, 0xba, 0xcb, 0xb6, 0xae, 0x7b, 0xaa, 0x7a, + 0x76, 0x6e, 0x75, 0x6d, 0x5e, 0x5b, 0x5d, 0x5a, 0x73, 0x6b, 0x72, 0x6a, 0xdb, 0xd7, 0xd9, 0xd5, + 0x56, 0x4e, 0x55, 0x4d, 0xbd, 0xb5, 0xbb, 0xad, 0x53, 0x4b, 0x52, 0xdd, 0xab, 0x4a, 0xa9, 0x49 + } +}; + +static const int DECODE[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x02, 0x12, -1, 0x14, 0x16, 0x68, + -1, -1, 0x18, 0x1a, 0x12, 0x1c, 0x1e, 0x6a, -1, 0x6c, 0x24, 0x26, 0x14, 0x2c, 0x2e, 0x18, + -1, -1, -1, -1, 0x16, 0x1a, 0x38, 0x3a, -1, 0x6e, 0x30, 0x32, 0x46, 0x34, 0x36, 0x70, + -1, -1, 0x38, 0x3a, 0x54, 0x3c, 0x3e, 0x72, -1, 0x74, 0x40, 0x42, 0x56, 0x44, 0x46, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 0x68, 0x6a, 0x58, 0x80, 0x6c, 0x8a, -1, 0x82, 0x6e, 0x88, 0x5a, 0xa2, 0x70, 0x5c, + -1, -1, -1, -1, -1, 0x00, 0x44, 0x02, -1, 0x08, 0x48, 0x0a, -1, 0x04, 0x4a, 0x76, + -1, -1, 0x4c, 0x4e, -1, 0x06, 0x50, 0x78, -1, 0x7a, 0x52, 0x0c, -1, 0x0e, 0x54, 0x46, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x7c, 0x56, 0x58, -1, 0x5a, 0x5c, 0x7e, + -1, -1, 0x5e, 0x60, -1, 0x20, 0x62, 0x22, -1, 0x28, 0x64, 0x2a, -1, 0x10, 0x66, 0x4c, + -1, -1, -1, -1, -1, 0xa0, 0x4e, 0x5e, -1, 0xaa, 0x72, 0xa8, -1, 0x74, 0x76, 0x60, + -1, -1, 0x78, 0x7a, -1, 0x7c, 0x7e, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +static unsigned char +encode_read_diff(const int encode[64], unsigned char *accu, unsigned char *carry, unsigned char value) +{ + unsigned char value_to_encode; + + unsigned char target = DECODE[encode[value]] & 0x7e; + for (value_to_encode = 0; value_to_encode < 64; ++value_to_encode) { + unsigned char val = DECODE[encode[value_to_encode]] + *accu + *carry; + if ((val & 0x7e) == target) { + break; + } + } + if (value_to_encode >= 64) { + printf("Encoding error, 0x%x = 0x%x + %d + ?\n", target, *accu, *carry); + for (value_to_encode = 0; value_to_encode < 64; ++value_to_encode) { + unsigned char val = DECODE[encode[value_to_encode]] + *accu + *carry; + printf("%d: 0x%x <- 0x%x\n", value_to_encode, val & 0x7e, DECODE[encode[value_to_encode]]); + } + + fprintf(stderr, "ERROR: Transwarp encoding error\n"); + exit(-2); + } + + int sum = DECODE[encode[value_to_encode]] + *accu + *carry; + *accu = sum; + *carry = sum >= 256; + + unsigned char check = DECODE[encode[value]]; + if ((*accu & 0x7e) != (check & 0x7e)) { + fprintf(stderr, "ERROR: Transwarp encoding error, 0x%x: actual 0x%x != 0x%x expected\n", DECODE[encode[check & 0x3f]], *accu, check); + exit(-3); + } + + unsigned char temp = (*carry << 7) | (*accu >> 1); + *carry = *accu & 1; + *accu = (*carry << 7) | ((temp & 0xfb) >> 1); + *carry = (*accu >> 6) & 1; + + return encode[value_to_encode]; +} + +unsigned char +encode_send_diff(unsigned char value, unsigned char *accu, unsigned char *carry) +{ + value = (value & ~((1 << 3) | (1 << 0))) + | (((value >> 3) & 1) << 0) + | (((value >> 0) & 1) << 3); + value ^= 0xff; + + int diff = value - *accu - *carry; + *carry = (diff < 0); + + *accu = value; + + *accu = (((*accu >> 7) & 1) << 7) + | (((*accu >> 1) & 1) << 6) + | (((*accu >> 7) & 1) << 5) + | (*carry << 4) + | (((*accu >> 6) & 1) << 2) + | (((*accu >> 5) & 1) << 1); + *carry = (*accu >> 6) & 1; + + return diff; +} + +static unsigned char +encode_receive_diff(const transwarp_encode_context *ctx, unsigned char in, unsigned char *previous, unsigned char *carry) +{ + int offset = (ctx->version <= 84) ? 0 : 2; + + int out = in - *carry - offset; + int diff = out - ((*previous & 0xc0) | (out & 0x3f)); + *carry = (diff < 0); + out = ((out ^ *previous) & 0x3f) | diff; + *previous = in; + + return out; +} + +static unsigned char +encode_buffer_byte(const int encode[][64], unsigned char previous, unsigned char *carry, unsigned char in, unsigned char *out) +{ + unsigned char even = (previous >> 1) ^ in; + unsigned char odd = ((*carry << 7) | (previous >> 1)) ^ in; + + *carry = previous & 1; + + out[(31 * 5) + 4] = encode[2][even_bits(even)]; + out[4] = encode[2][odd_bits(odd)]; + + return in; +} + +static void +encode_base_bytes(const unsigned char scramble[][256], + transwarp_encode_context *ctx, const unsigned char in[3], unsigned char *out) +{ + unsigned char in0 = encode_receive_diff(ctx, in[0], &(ctx->previous), &(ctx->recvcarry)); + unsigned char in1 = encode_receive_diff(ctx, in[1], &(ctx->previous), &(ctx->recvcarry)); + unsigned char in2 = encode_receive_diff(ctx, in[2], &(ctx->previous), &(ctx->recvcarry)); + + in0 = scramble[0][in0]; + in1 = scramble[1][in1]; + in2 = scramble[2][in2]; + + unsigned char val3 = in0 & 0x3f; + out[0] = encode_read_diff(ENCODE[3], &(ctx->accu), &(ctx->carry), val3); + + unsigned char val4 = ((in0 >> 6) + | (in1 << 2)) & 0x3f; + out[1] = encode_read_diff(ENCODE[4], &(ctx->accu), &(ctx->carry), val4); + + unsigned char val0 = ((in1 >> 4) + | (in2 << 4)) & 0x3f; + out[2] = encode_read_diff(ENCODE[0], &(ctx->accu), &(ctx->carry), val0); + + unsigned char val1 = in2 >> 2; + out[3] = encode_read_diff(ENCODE[1], &(ctx->accu), &(ctx->carry), val1); +} + +static unsigned char +crc8(unsigned char value) +{ + for (int i = 0; i < 8; ++i) { + value = (value & 0x80) ? ((value << 1) ^ 0x31) : (value << 1); + } + + return value; +} + +static bool +decode_5_bytes_gcr(const int8_t decoding_map[], const unsigned char *in, unsigned char *out) +{ + unsigned char gcr_hi; + unsigned char gcr_lo; + + gcr_hi = in[0] >> 3; + gcr_lo = ((in[0] & 0x7) << 2) | (in[1] >> 6); + int out0 = (decoding_map[gcr_hi] << 4) | decoding_map[gcr_lo]; + + gcr_hi = (in[1] & 0x3e) >> 1; + gcr_lo = ((in[1] & 0x1) << 4) | (in[2] >> 4); + int out1 = (decoding_map[gcr_hi] << 4) | decoding_map[gcr_lo]; + + gcr_hi = ((in[2] & 0xf) << 1) | (in[3] >> 7); + gcr_lo = (in[3] & 0x7c) >> 2; + int out2 = (decoding_map[gcr_hi] << 4) | decoding_map[gcr_lo]; + + gcr_hi = ((in[3] & 0x3) << 3) | (in[4] >> 5); + gcr_lo = in[4] & 0x1f; + int out3 = (decoding_map[gcr_hi] << 4) | decoding_map[gcr_lo]; + + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = out3; + + return (out0 | out1 | out2 | out3) >= 0; +} + +static int +decode_gcr_block(const int8_t decoding_map[], const unsigned char *encoded, unsigned char decoded[]) +{ + bool ok = decode_5_bytes_gcr(decoding_map, encoded, decoded); + decoded[0] = decoded[1]; + decoded[1] = decoded[2]; + decoded[2] = decoded[3]; + + int computed_checksum = decoded[0] ^ decoded[1] ^ decoded[2]; + + int i = 5; + for (int j = 3; i < 320; i += 5, j += 4) { + ok &= decode_5_bytes_gcr(decoding_map, encoded + i, decoded + j); + computed_checksum ^= decoded[j] ^ decoded[j + 1] ^ decoded[j + 2] ^ decoded[j + 3]; + } + + unsigned char last_byte[4]; + ok &= decode_5_bytes_gcr(decoding_map, encoded + i, last_byte); + decoded[255] = last_byte[0]; + + computed_checksum ^= last_byte[0]; + computed_checksum = ok ? computed_checksum : -1; + + return ok ? computed_checksum : -1; +} + +static int +encode_transwarp_block(const unsigned char scramble[][256], const int8_t gcr_to_nibble[32], + transwarp_encode_context* ctx, const unsigned char *indata, int filepos, unsigned char encoded[325]) +{ + const unsigned char *unencoded = indata + filepos; + + unsigned char data[TRANSWARPBLOCKSIZE]; + if (filepos < 2) { + for (int i = filepos, j = 0; j < TRANSWARPBLOCKSIZE; ++i, ++j) { + data[j] = (i < 0) ? ' ' : ((i < 2) ? 0 : indata[i]); + } + + unencoded = data; + } + + unsigned char semiencoded[TRANSWARPBUFFERBLOCKSIZE]; + + for (int i = TRANSWARPBUFFERBLOCKSIZE - 1; i >= 0; --i) { + unsigned char value = encode_receive_diff(ctx, unencoded[TRANSWARPBASEBLOCKSIZE + i], &(ctx->previous2), &(ctx->carry2)); + + value = scramble[3][value]; + + semiencoded[i] = encode_send_diff(value, &(ctx->sendaccu), &(ctx->sendcarry)); + } + + unsigned char buffer_previous = ctx->previous1; + unsigned char buffer_carry = 0; + for (int i = 0; i < TRANSWARPBUFFERBLOCKSIZE; ++i) { + int shuffle = (TRANSWARPBUFFERBLOCKSIZE - 1) - (i / 2) - ((i & 1) ? ((TRANSWARPBUFFERBLOCKSIZE / 2) + 1) : 0); + unsigned char value = semiencoded[shuffle]; + buffer_previous = encode_buffer_byte(ENCODE, buffer_previous, &buffer_carry, value, encoded + 3 + (5 * (TRANSWARPBUFFERBLOCKSIZE - 1 - shuffle))); + } + + unsigned char previous = unencoded[TRANSWARPBASEBLOCKSIZE - 1]; + + const int CRCSTEP = 8; + + for (int i = 0; i < TRANSWARPBASEBLOCKSIZE; i += CRCSTEP) { + previous = crc8(previous); + previous ^= unencoded[i]; + } + previous = crc8(previous); + + if (filepos > ((21 * TRANSWARPBLOCKSIZE) + 2)) { + const int BACKCHECKOFFS = (21 * TRANSWARPBLOCKSIZE) + TRANSWARPBUFFERBLOCKSIZE; + previous ^= indata[filepos - BACKCHECKOFFS]; + previous = crc8(previous); + previous ^= indata[filepos - BACKCHECKOFFS + TRANSWARPBUFFERBLOCKSIZE - 1]; + previous = crc8(previous); + } + + char head_data[4] = { 7, 0, 0, 0 }; + encode_4_bytes_gcr(head_data, (char *) encoded); + char tail_data[4] = { 0, 0, 0, 0 }; + encode_4_bytes_gcr(tail_data, (char *) encoded + 320); + + unsigned char accu = ctx->previous; + unsigned char carry = 0; + for (int i = 0; i < TRANSWARPBASEBLOCKSIZE; ++i) { + encode_receive_diff(ctx, unencoded[i], &accu, &carry); + } + + unsigned char receive_checksum = (-previous - carry); + + unsigned char checksum = receive_checksum ^ (buffer_previous >> 1); + + unsigned char odd = odd_bits(checksum); + + odd ^= (buffer_carry << 3); + + static const unsigned char ENCODE_TOP[] = { + 0x05, + 0x07, + 0x0d, + 0x0b + }; + encoded[2] = (encoded[2] & 0xf0) | ENCODE_TOP[odd & 0x3]; + + encoded[317] = ENCODE[2][even_bits(checksum)]; + + encoded[322] = ENCODE[2][odd & 0xc]; + + unsigned char top_2_bits = encoded[2]; + unsigned char middle_4_bits = encoded[317]; + unsigned char bottom_2_bits = encoded[322]; + + ctx->recvcarry = 0; + + ctx->accu = 0; + ctx->carry = 0; + for (int i = 0, j = 0; i < TRANSWARPBASEBLOCKSIZE; i += 3, j += 5) { + encode_base_bytes(scramble, ctx, unencoded + i, encoded + 3 + j); + + ctx->accu = 8; + ctx->carry = 0; + + unsigned char target = encoded[3 + j + 4]; + unsigned char target_accu = DECODE[target]; + unsigned char target_check = ENCODE[2][odd_bits(target_accu)]; + + if (target != target_check) { + fprintf(stderr, "ERROR: Transwarp encoding error, [%d] 0x%x != 0x%x <- 0x%x\n", i, target, target_check, DECODE[target]); + + exit(-4); + } + + unsigned char store = ctx->accu ^ target_accu; + encoded[3 + j + 4] = ENCODE[2][odd_bits(store)]; + + unsigned char stored = DECODE[encoded[3 + j + 4]]; + ctx->accu ^= stored; + + if (ctx->accu != target_accu) { + fprintf(stderr, "ERROR: Transwarp encoding error, [%d] actual 0x%x != 0x%x expected <- 0x%x\n", i, ctx->accu, target_accu, stored); + + exit(-5); + } + } + if (carry != ctx->recvcarry) { + fprintf(stderr, "ERROR: Transwarp encoding error, carry %d != %d recvcarry\n", carry, ctx->recvcarry); + + exit(-6); + } + + unsigned char top_fix = encoded[2] ^ (DECODE[encoded[322]] & 0xf); + top_fix = ((top_fix >> 2) & 2) | ((top_fix >> 1) & 1); + encoded[2] = (encoded[2] & 0xf0) | ENCODE_TOP[top_fix]; + encoded[322] = (encoded[322] & 0xf0) | 0x5; + + unsigned char block_checksum; + block_checksum = DECODE[middle_4_bits]; + block_checksum = (block_checksum >> 1) | (buffer_carry << 7); + block_checksum ^= DECODE[bottom_2_bits]; + block_checksum ^= (top_2_bits & 0xa); + + if (block_checksum != checksum) { + fprintf(stderr, "ERROR: Transwarp encoding error, actual 0x%x != 0x%x expected, 0x%x -> [0x%x] -> 0x%x -> 0x%x -> [0x%x]\n", block_checksum, checksum, checksum & 0xaa, ((checksum & 0xaa) >> 1) | (checksum & 0xaa), odd, ENCODE[2][odd], DECODE[encoded[317]]); + + exit(-1); + } + + unsigned char gcr_decoded[4]; + bool ok = decode_5_bytes_gcr(gcr_to_nibble, encoded + 320, gcr_decoded); + int gcr_checksum = ok ? gcr_decoded[1] : -1; + + unsigned char decoded[256]; + int computed_checksum = decode_gcr_block(gcr_to_nibble, encoded, decoded); + + ok &= decode_5_bytes_gcr(gcr_to_nibble, encoded, gcr_decoded); + gcr_decoded[1] ^= gcr_checksum ^ computed_checksum; + + encode_4_bytes_gcr((char *) gcr_decoded, (char *) encoded); + + return ok == 0; +} + +static void +permute(unsigned char *key, int len, int *set) +{ + for (int i = 0; i < (len - 1); ++i) { + int divisor = len - i; + + int remainder = 0; + for (int i = TRANSWARPKEYSIZE - 12; i >= 0; --i) { + int dividend = (remainder << 8) | key[i]; + remainder = dividend % divisor; + key[i] = dividend / divisor; + } + + int n = set[remainder]; + set[remainder] = set[divisor - 1]; + set[divisor - 1] = n; + } +} + +/* Write file to disk using Transwarp encoding */ +static unsigned long long +write_transwarp_file(image_type type, unsigned char *image, imagefile *file, unsigned char *filedata, int *filesize, unsigned int version, bool transwarp_bootfile_fits_on_dir_track) +{ + file->size = *filesize - 2; + + unsigned int track = DIRTRACK_D41_D71 - 1; + + if ((file->mode & MODE_MIN_TRACK_MASK) > 0) { + /* for Transwarp files, a set minimum track is the file's starting track */ + track = (file->mode & MODE_MIN_TRACK_MASK) >> MODE_MIN_TRACK_SHIFT; + } else { + /* allocate */ + bool free_tracks[40]; + for (unsigned int t = 1; t <= image_num_tracks(type); ++t) { + bool track_free = true; + for (int sector = 0; sector < num_sectors(type, t); ++sector) { + if (is_sector_free(type, image, t, sector, 0 /* numdirblocks */, 0 /* dir_sector_interleave */) == false) { + track_free = false; + break; + } + } + free_tracks[t - 1] = track_free; + } + + /* below dir track */ + while (track > 0) { + if (free_tracks[track - 1]) { + int filesize = file->size; + int t = track; + while ((filesize > 0) + && (t > 0)) { + filesize -= (TRANSWARPBLOCKSIZE * num_sectors(type, t)); + if (filesize >= 0) { + if (free_tracks[t - 1] == false) { + break; + } + --t; + } + } + + if (filesize <= 0) { + break; + } + } + --track; + } + + if (track <= 0) { + /* above dir track */ + track = DIRTRACK_D41_D71 + (transwarp_bootfile_fits_on_dir_track ? 1 : 2); + while (track <= image_num_tracks(type)) { + if (free_tracks[track - 1]) { + int filesize = file->size; + int t = track; + while (filesize > 0) { + filesize -= (TRANSWARPBLOCKSIZE * num_sectors(type, t)); + if (filesize >= 0) { + if (free_tracks[t - 1] == false) { + break; + } + ++t; + } + } + + if (filesize <= 0) { + break; + } + } + ++track; + } + } + } + + file->track = track; + file->sector = 0; + + int8_t gcr_to_nibble[32]; + generate_gcr_decoding_table(NIBBLE_TO_GCR, gcr_to_nibble); + + unsigned char key[TRANSWARPKEYSIZE]; + memset(key, 0, sizeof key); + + if (file->have_key != 0) { + if ((filedata[0] == 0x01) + && (filedata[1] == 0x08)) { + srand((unsigned int) time(NULL)); + + unsigned int linelink = ((filedata[3] << 8) | filedata[2]) - 0x0801 + 2; + if ((linelink > 0) + && ((linelink - 2) < (unsigned int) file->size) + && ((filedata[linelink - 1] | filedata[linelink] | filedata[linelink + 1]) == 0)) { + while (filedata[linelink] == 0) { + filedata[linelink] = rand(); + } + } + + filedata[2] = rand(); + while ((filedata[3] == 0) + || (filedata[3] == 8)) { + filedata[3] = rand(); + } + + int file_size = file->size; + int filetrack = track; + while (file_size > 0) { + file_size -= (TRANSWARPBLOCKSIZE * num_sectors(type, filetrack)); + if (file_size > 0) { + filetrack = (filetrack < DIRTRACK_D41_D71) ? (filetrack - 1) : (filetrack + 1); + } + } + int spare_blocks = (0 - file_size) / TRANSWARPBLOCKSIZE; + int spare_bytes = TRANSWARPBLOCKSIZE - (file->size % TRANSWARPBLOCKSIZE); + spare_bytes = (spare_blocks * TRANSWARPBLOCKSIZE) + ((spare_bytes != TRANSWARPBLOCKSIZE) ? spare_bytes : 0); + while (spare_bytes > (0x0801 - 0x0400)) { + spare_bytes -= TRANSWARPBLOCKSIZE; + } + if (spare_bytes > 0) { + int loadaddress = (filedata[1] << 8) | filedata[0]; + loadaddress -= spare_bytes; + filedata[0] = loadaddress; + filedata[1] = loadaddress >> 8; + memmove(filedata + 2 + spare_bytes, filedata + 2, file->size); + filedata[spare_bytes + 1] = 0; + for (int i = 2; i <= spare_bytes; ++i) { + filedata[i] = rand(); + } + file->size += spare_bytes; + *filesize += spare_bytes; + } + } + + memcpy(key, file->key, sizeof key); + + for (int round = TRANSWARPKEYHASHROUNDS; round > 0; --round) { + for (int i = 0; i < (TRANSWARPKEYSIZE - 1); ++i) { + key[i] ^= key[i + 1]; + } + + for (int i = TRANSWARPKEYSIZE - 1; i >= 0; --i) { + int product = key[i] * 0x6b; + key[i] = product; + int msb = product >> 8; + for (int j = i + 1; j < TRANSWARPKEYSIZE; ++j) { + msb += key[j]; + key[j] = msb; + msb >>= 8; + } + } + + int sum = round; + for (int i = 0; i < TRANSWARPKEYSIZE; ++i) { + sum += key[i]; + key[i] = sum; + sum >>= 8; + } + } + } + + unsigned long long dirdatakey = (key[TRANSWARPKEYSIZE - 1] * (1ULL << 56)) + + (key[TRANSWARPKEYSIZE - 2] * (1ULL << 48)) + + (key[TRANSWARPKEYSIZE - 3] * (1ULL << 40)) + + (key[TRANSWARPKEYSIZE - 4] * (1ULL << 32)) + + (key[TRANSWARPKEYSIZE - 5] * (1ULL << 24)) + + (key[TRANSWARPKEYSIZE - 6] * (1ULL << 16)) + + (key[TRANSWARPKEYSIZE - 7] * (1ULL << 8)) + + key[TRANSWARPKEYSIZE - 8]; + + int initial_buffer_store_value = key[TRANSWARPKEYSIZE - 9]; + int initial_buffer_recvaccu_value = key[TRANSWARPKEYSIZE - 10]; + int initial_block_recvaccu_value = key[TRANSWARPKEYSIZE - 11]; + + unsigned char scramble[4][256]; + for (int i = 0; i < 4; ++i) { + int set[] = { 0, 2, 4, 1, 3, 5 }; + if (file->have_key) { + permute(key, 6, set); + } + + int set2[3][4]; + for (int j = 0; j < 3; ++j) { + int set3[] = { 0, 1, 2, 3 }; + if (file->have_key) { + permute(key, 4, set3); + } + for (int k = 0; k < 4; ++k) { + for (int l = 0; l < 4; ++l) { + if (k == set3[l]) { + set2[j][k] = l; + break; + } + } + } + } + + for (int j = 0; j < 256; ++j) { + unsigned char scrambled = (set2[0][(((j >> set[0]) & 1) << 0) + | (((j >> set[3]) & 1) << 1)] << 0) + | (set2[1][(((j >> set[1]) & 1) << 0) + | (((j >> set[4]) & 1) << 1)] << 2) + | (set2[2][(((j >> set[2]) & 1) << 0) + | (((j >> set[5]) & 1) << 1)] << 4) + | (j & 0xc0); + scramble[i][j] = scrambled; + } + } + + int sectors[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + if (file->have_key != 0) { + permute(key, 17, sectors); + } + + transwarp_encode_context ctx; + memset(&ctx, 0, sizeof ctx); + ctx.version = version; + + ctx.previous1 = initial_buffer_store_value; + + bool done = false; + int block_index = 0; + int total_blocks = 0; + + int filepos = 2; + + for (; !done; (track >= DIRTRACK_D41_D71) ? ++track : --track) { + if ((track < 1) + || (track > image_num_tracks(type))) { + fprintf(stderr, "ERROR: Disk full (track %d out of range) while writing Transwarp file ", track); + print_filename(stderr, file->pfilename); + fprintf(stderr, "\n"); + exit(-4); + } + + int next_track_pos = filepos + (num_sectors(type, track) * TRANSWARPBLOCKSIZE); + bool last_track = (next_track_pos >= *filesize); + if (last_track) { + int sector = 0; + for (int s = 0; s < num_sectors(type, track); ++s) { + sectors[s] = sector; + if ((filepos + ((sector + 1) * TRANSWARPBLOCKSIZE)) >= *filesize) { + sector = 0; + } else { + ++sector; + } + } + } + int next_track_block_index = block_index + num_sectors(type, track); + int trackpos = filepos; + + transwarp_encode_context trackctx = ctx; + + for (int sector = 0; sector < num_sectors(type, track); ++sector) { + if (is_sector_free(type, image, track, sector, 0 /* numdirblocks */, 0 /* dir_sector_interleave */) == false) { + fprintf(stderr, "ERROR: t%d/s%d not free for Transwarp file ", track, sector); + print_filename(stderr, file->pfilename); + fprintf(stderr, "\n"); + check_bam(type, image); + + exit(-5); + } + + bool last_block = false; + int pos = trackpos + (sectors[sector] * TRANSWARPBLOCKSIZE); + if ((pos + TRANSWARPBLOCKSIZE) >= *filesize) { + pos = *filesize - TRANSWARPBLOCKSIZE; + last_block = true; + } + + unsigned char encoded[320 + 5]; + unsigned char previous = block_index + sectors[sector]; + previous ^= initial_block_recvaccu_value; + + ctx.previous = previous; + ctx.previous2 = initial_buffer_recvaccu_value; + + int error = encode_transwarp_block((const unsigned char (*)[256]) scramble, gcr_to_nibble, &ctx, filedata, pos, encoded); + if (error) { + fprintf(stderr, "ERROR: encoding error on t%d/s%d\n", track, sector); + + exit(-6); + } + + unsigned char decoded[256]; + int checksum = decode_gcr_block(gcr_to_nibble, encoded, decoded); + if (checksum < 0) { + fprintf(stderr, "ERROR: decoding error on t%d/s%d\n", track, sector); + + exit(-7); + } + + int offset = linear_sector(type, track, sector) * BLOCKSIZE; + memcpy(image + offset, decoded, BLOCKSIZE); + + ++total_blocks; + + if (last_block) { + ctx = trackctx; + done = true; + } + + mark_sector(type, image, track, sector, 0 /* not free */); + } + + filepos = next_track_pos; + block_index = next_track_block_index; + file->last_track = track; + } + + file->nrSectors = total_blocks; + + return dirdatakey; +} + +/* Write files to disk */ +static void +write_files(image_type type, unsigned char *image, imagefile *files, int num_files, int usedirtrack, int dirtracksplit, int shadowdirtrack, int numdirblocks, int dir_sector_interleave) +{ + unsigned char track = 1; + unsigned char sector = 0; + int bytes_to_write = 0; + int lastTrack = track; + int lastSector = sector; + int lastOffset = linear_sector(type, lastTrack, lastSector) * BLOCKSIZE; + int lastMinTrack = track; + bool transwarp_bootfile_fits_on_dir_track = false; + + /* make sure the first file already takes first sector per track into account */ + if (num_files > 0) { + sector = (type == IMAGE_D81) ? 0 : files[0].first_sector_new_track; + } + + int transwarp_version = 100; + + for (int i = 0; i < num_files; i++) { + imagefile *file = files + i; + if ((file->mode & MODE_TRANSWARPBOOTFILE) != 0) { + int fileSize = 0; + + struct stat st; + if (stat((char*)files[i].alocalname, &st) == 0) { + fileSize = (int)st.st_size; + } + + int version_major; + int version_minor; + if (sscanf((char *) basename(files[i].alocalname), "transwarp v%d.%d", &version_major, &version_minor) == 2) { + transwarp_version = (version_major * 100) + version_minor; + } + + int num_blocks = (fileSize / 254) + ((fileSize % 254 == 0) ? 0 : 1); + transwarp_bootfile_fits_on_dir_track = (num_blocks <= 17); + + break; + } + } + + for (int i = 0; i < num_files; i++) { + imagefile *file = files + i; + if (type == IMAGE_D81) { + file->sectorInterleave = 1; /* caught in command line parsing anyway, but does not hurt */ + } + + int file_usedirtrack = usedirtrack; + int file_numdirblocks = numdirblocks; + + if ((file->mode & MODE_NOFILE)) { + if (file->nrSectorsShown == -1) { + file->nrSectorsShown = file->nrSectors; + } + file->track = 0; + file->sector = 0; + int entryOffset = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[entryOffset + FILETRACKOFFSET] = file->track; + image[entryOffset + FILESECTOROFFSET] = file->sector; + image[entryOffset + FILEBLOCKSLOOFFSET] = file->nrSectorsShown & 255; + image[entryOffset + FILEBLOCKSHIOFFSET] = file->nrSectorsShown >> 8; + if (shadowdirtrack > 0) { + entryOffset = linear_sector(type, shadowdirtrack, file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[entryOffset + FILETRACKOFFSET] = file->track; + image[entryOffset + FILESECTOROFFSET] = file->sector; + image[entryOffset + FILEBLOCKSLOOFFSET] = file->nrSectors & 255; + image[entryOffset + FILEBLOCKSHIOFFSET] = file->nrSectors >> 8; + } + } else if (!(file->mode & MODE_LOOPFILE)) { /* loop files are handled later */ + + int fileSize = 0; + + struct stat st; + if (stat((char*)files[i].alocalname, &st) == 0) { + fileSize = (int)st.st_size; + } + + if ((file->mode & MODE_TRANSWARPBOOTFILE) != 0) { + file_usedirtrack = true; + file_numdirblocks = transwarp_bootfile_fits_on_dir_track ? 2 : 4; + file->sectorInterleave = -4; + file->mode = (file->mode & ~MODE_BEGINNING_SECTOR_MASK) | (10 + 1); + file->first_sector_new_track = 10; + track = DIRTRACK_D41_D71; + } + + unsigned char* filedata = (unsigned char*)calloc(fileSize + ((file->filetype & FILETYPETRANSWARPMASK) ? (21 * TRANSWARPBLOCKSIZE) : 0), sizeof(unsigned char)); + if (filedata == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + + exit(-1); + } + FILE* f = fopen((char*)file->alocalname, "rb"); + if (f == NULL) { + fprintf(stderr, "ERROR: Could not open file \"%s\" for reading\n", file->alocalname); + + exit(-1); + } + if (fread(filedata, fileSize, 1, f) != 1) { + fprintf(stderr, "ERROR: Unexpected filesize when reading %s\n", file->alocalname); + exit(-1); + } + fclose(f); + + if ((!(file->filetype & FILETYPETRANSWARPMASK)) + && ((file->mode & MODE_MIN_TRACK_MASK) > 0)) { + int minTrack = (file->mode & MODE_MIN_TRACK_MASK) >> MODE_MIN_TRACK_SHIFT; + if (lastMinTrack != minTrack) { + lastMinTrack = minTrack; + track = minTrack; + /* note that track may be smaller than lastTrack now */ + if (track > image_num_tracks(type)) { + fprintf(stderr, "ERROR: Invalid minimum track %u for file %s (", track, file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ") specified\n"); + + exit(-1); + } + while ((!file_usedirtrack) + && ((track == dirtrack(type)) + || (track == shadowdirtrack) + || ((type == IMAGE_D71) && (track == (D64NUMTRACKS + dirtrack(type)))))) { /* .d71 track 53 is usually empty except the extra BAM block */ + ++track; /* skip dir track */ + } + if (abs(((int) track) - lastTrack) > 1) { + /* previous file's last track and this file's beginning track have tracks in between */ + sector = (type == IMAGE_D81) ? 0 : file->first_sector_new_track; + } + } + } else { + lastMinTrack = 0; + } + + if ((file->mode & MODE_BEGINNING_SECTOR_MASK) > 0) { + sector = (file->mode & MODE_BEGINNING_SECTOR_MASK) - 1; + } + + if ((!(file->filetype & FILETYPETRANSWARPMASK)) + && (((file->mode & MODE_SAVETOEMPTYTRACKS) != 0) + || ((file->mode & MODE_FITONSINGLETRACK) != 0))) { + + /* find first empty track */ + int found = 0; + while (!found) { + for (int s = 0; s < num_sectors(type, track); s++) { + if (is_sector_free(type, image, track, s, file_usedirtrack ? file_numdirblocks : 0, dir_sector_interleave)) { + if (s == (num_sectors(type, track) - 1)) { + found = 1; + /* In first pass, use sector as left by previous file (or as set by -b) to reach first file block quickly. */ + /* Claus: according to Krill, on real HW tracks are not aligned anyway, so it does not make a difference. */ + /* Emulators tend to reset the disk angle on track changes, so this should rather be 3. */ + if (sector >= num_sectors(type, track)) { + if ((file->mode & MODE_BEGINNING_SECTOR_MASK) > 0) { + fprintf(stderr, "ERROR: Invalid beginning sector %u on track %u for file %s (", sector, track, file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ") specified\n"); + + exit(-1); + } + + sector %= num_sectors(type, track); + } + } + } else { + int prev_track = track; + if (file->mode & MODE_SAVECLUSTEROPTIMIZED) { + if (track > D64NUMTRACKS) { + int next_track = track - D64NUMTRACKS + 1; /* to next track on first side */ + if (next_track < D64NUMTRACKS) { + track = next_track; + } else { + ++track; /* disk full */ + } + } else { + track += D64NUMTRACKS; /* to same track on second side */ + } + } else { + ++track; + } + while ((!file_usedirtrack) + && ((track == dirtrack(type)) + || (track == shadowdirtrack) + || ((type == IMAGE_D71) && (track == D64NUMTRACKS + dirtrack(type))))) { /* .d71 track 53 is usually empty except the extra BAM block */ + ++track; /* skip dir track */ + } + if (file->mode & MODE_FITONSINGLETRACK) { + int file_size = fileSize; + int first_sector = -1; + for (int s = 0; s < num_sectors(type, prev_track); s++) { + if (is_sector_free(type, image, prev_track, s, file_usedirtrack ? file_numdirblocks : 0, dir_sector_interleave)) { + if (first_sector < 0) { + first_sector = s; + } + file_size -= BLOCKSIZE + BLOCKOVERHEAD; + if (file_size <= 0) { + found = 1; + track = prev_track; + sector = first_sector; + break; + } + } + } + } + + if (track > image_num_tracks(type)) { + fprintf(stderr, "ERROR: Disk full, file %s (", file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ")\n"); + + exit(-1); + } + break; + } + } /* for each sector on track */ + + if ((track == (lastTrack + 2)) + && ((file->mode & MODE_BEGINNING_SECTOR_MASK) == 0)) { + /* previous file's last track and this file's beginning track have tracks in between now */ + sector = 0; + } + } /* while not found */ + } + + if ((file->mode & MODE_BEGINNING_SECTOR_MASK) > 0) { + if (sector != ((file->mode & MODE_BEGINNING_SECTOR_MASK) - 1)) { + fprintf(stderr, "ERROR: Specified beginning sector of file %s (", file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ") not free on track %u\n", track); + + exit(-1); + } + } + + /* found start track, now save file */ + if (type == IMAGE_D81) { + sector = 0; + } + + int byteOffset = 0; + int bytesLeft = fileSize; + + unsigned long long key0 = 0; + if (file->filetype & FILETYPETRANSWARPMASK) { + key0 = write_transwarp_file(type, image, file, filedata, &fileSize, transwarp_version, transwarp_bootfile_fits_on_dir_track); + + bytesLeft = 0; + } + + while (bytesLeft > 0) { + /* Find free track & sector, starting from current T/S forward one revolution, then the next track etc... skip dirtrack (unless -t is active) */ + /* If the file didn't fit before dirtrack then restart on dirtrack + 1 and try again (unless -t is active). */ + /* If the file didn't fit before track 36/41/71 then the disk is full. */ + + int blockfound = 0; + int findSector = 0; + + while (!blockfound) { + /* find spare block on the current track */ + for (int s = sector; s < sector + num_sectors(type, track); s++) { + findSector = s % num_sectors(type, track); + + if (is_sector_free(type, image, track, findSector, file_usedirtrack ? file_numdirblocks : 0, dir_sector_interleave)) { + blockfound = 1; + break; + } + } + + if (!blockfound) { + /* find next track, use some magic to make up for track seek delay */ + int seek_delay = 1; + if (file->mode & MODE_SAVECLUSTEROPTIMIZED) { + if (track > D64NUMTRACKS) { + track = track - D64NUMTRACKS + 1; + } else { + track += D64NUMTRACKS; + seek_delay = 0; /* switching to the other side, no head movement */ + } + } else { + ++track; + } + if (type == IMAGE_D81) { + sector = 0; + } else if (file->first_sector_new_track < 0) { + sector -= file->first_sector_new_track; + } else if ((file->sectorInterleave < 0) + && ((file->mode & MODE_TRANSWARPBOOTFILE) == 0)) { + sector += seek_delay; + } else { + sector = file->first_sector_new_track; + } + sector %= num_sectors(type, track); + findSector = sector; + + while ((!file_usedirtrack) + && ((track == dirtrack(type)) + || (track == shadowdirtrack) + || ((type == IMAGE_D71) && (track == D64NUMTRACKS + dirtrack(type))))) { /* .d71 track 53 is usually empty except the extra BAM block */ + /* Delete old fragments and restart file */ + if (!dirtracksplit) { + if (file->nrSectors > 0) { + int deltrack = file->track; + int delsector = file->sector; + while (deltrack != 0) { + int b = linear_sector(type, deltrack, delsector); + if(b < 0) { + break; + } + int offset = b * BLOCKSIZE; + mark_sector(type, image, deltrack, delsector, 1 /* free */); + deltrack = image[offset + 0]; + delsector = image[offset + 1]; + memset(image + offset, 0, BLOCKSIZE); + } + } + + bytesLeft = fileSize; + byteOffset = 0; + file->nrSectors = 0; + } + ++track; + } + + if (track > image_num_tracks(type)) { + if (verbose) { + print_file_allocation(type, image, files, num_files); + check_bam(type, image); + } + + fprintf(stderr, "ERROR: Disk full, file %s (", file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ")\n"); + free(filedata); + + exit(-1); + } + } + } /* while not block found */ + + sector = findSector; + int offset = linear_sector(type, track, sector) * BLOCKSIZE; + + if (bytesLeft == fileSize) { + file->track = track; + file->sector = sector; + lastTrack = track; + lastSector = sector; + lastOffset = offset; + } else { + image[lastOffset + 0] = track; + image[lastOffset + 1] = sector; + } + + /* write sector */ + bytes_to_write = min(BLOCKSIZE - BLOCKOVERHEAD, bytesLeft); + memset(image + offset + 2, 0, 254); + memcpy(image + offset + 2, filedata + byteOffset, bytes_to_write); + + bytesLeft -= bytes_to_write; + byteOffset += bytes_to_write; + + lastTrack = track; + lastSector = sector; + lastOffset = offset; + + mark_sector(type, image, track, sector, 0 /* not free */); + + if (num_sectors(type, track) <= abs(file->sectorInterleave)) { + fprintf(stderr, "ERROR: Invalid interleave %d on track %u (%d sectors), file %s (", file->sectorInterleave, track, num_sectors(type, track), file->alocalname); + print_filename(stderr, file->pfilename); + fprintf(stderr, ")\n"); + + exit(-1); + } + + sector += abs(file->sectorInterleave); + sector %= num_sectors(type, track); + + file->nrSectors++; + } /* while bytes left */ + + if (!(file->filetype & FILETYPETRANSWARPMASK)) { + image[lastOffset + 0] = 0x00; + image[lastOffset + 1] = bytes_to_write + 1; + } + + /* update directory entry */ + int entryOffset = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[entryOffset + FILETRACKOFFSET] = file->track; + image[entryOffset + FILESECTOROFFSET] = file->sector; + + if (file->filetype & FILETYPETRANSWARPMASK) { + image[entryOffset + FILETRACKOFFSET] = 0; + image[entryOffset + FILESECTOROFFSET] = 0; + + image[entryOffset + TRANSWARPSIGNATROFFSLO] = TRANSWARPSIGNATURELO; + image[entryOffset + TRANSWARPSIGNATROFFSHI] = TRANSWARPSIGNATUREHI; + + image[entryOffset + TRANSWARPTRACKOFFSET] = file->track; + + image[entryOffset + FILEBLOCKSLOOFFSET] = file->nrSectors; + image[entryOffset + FILEBLOCKSHIOFFSET] = (file->nrSectors >> 8); + if (image[entryOffset + FILEBLOCKSHIOFFSET] > 0) { + fprintf(stderr, "ERROR: Transwarp file \"%s\" is %d > 255 blocks big\n", file->alocalname, file->nrSectors); + + exit(-8); + } + + int loadaddress = (filedata[1] << 8) | filedata[0]; + image[entryOffset + LOADADDRESSLOOFFSET] = loadaddress; + image[entryOffset + LOADADDRESSHIOFFSET] = (loadaddress >> 8); + int endaddress = loadaddress + fileSize - 2; + image[entryOffset + ENDADDRESSLOOFFSET] = endaddress; + image[entryOffset + ENDADDRESSHIOFFSET] = (endaddress >> 8); + + unsigned char file_checksum = 0xff; + for (int i = 2; i < fileSize; ++i) { + file_checksum ^= filedata[i]; + file_checksum = crc8(file_checksum); + } + image[entryOffset + FILECHECKSUMOFFSET] = file_checksum; + + image[entryOffset + DIRDATACHECKSUMOFFSET] = 0; + image[entryOffset + DIRDATACHECKSUMOFFSET] = (0x0100 - transwarp_dirdata_checksum(image, entryOffset)); + unsigned char dirdata_checksum = transwarp_dirdata_checksum(image, entryOffset); + if (dirdata_checksum != 0) { + if (dirdata_checksum == 1) { + --image[entryOffset + DIRDATACHECKSUMOFFSET]; + } + dirdata_checksum = transwarp_dirdata_checksum(image, entryOffset); + if (dirdata_checksum != 0) { + fprintf(stderr, "ERROR: Encoding error with \"%s\", 0x%x\n", file->alocalname, dirdata_checksum); + + exit(-9); + } + } + + if (file->have_key != 0) { + for (int offset = DIRDATACHECKSUMOFFSET; offset <= FILEBLOCKSLOOFFSET; ++offset) { + image[entryOffset + offset] ^= key0; + key0 >>= 8; + } + + file->nrSectors = image[entryOffset + FILEBLOCKSLOOFFSET]; + } + } + + if (file->nrSectorsShown < 0) { + file->nrSectorsShown = file->nrSectors; + } + image[entryOffset + FILEBLOCKSLOOFFSET] = file->nrSectorsShown & 255; + image[entryOffset + FILEBLOCKSHIOFFSET] = file->nrSectorsShown >> 8; + + if (shadowdirtrack > 0) { + entryOffset = linear_sector(type, shadowdirtrack, file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[entryOffset + FILETRACKOFFSET] = file->track; + image[entryOffset + FILESECTOROFFSET] = file->sector; + + image[entryOffset + FILEBLOCKSLOOFFSET] = file->nrSectors & 255; + image[entryOffset + FILEBLOCKSHIOFFSET] = file->nrSectors >> 8; + } + + free(filedata); + } + } /* for each file */ + + /* Set track/sector of Transwarp file entries to Transwarp bootfile */ + int transwarp_boot_track = 0; + int transwarp_boot_sector = 0; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + for (int i = 0; i < num_files; i++) { + imagefile *file = files + i; + if (file->filetype & FILETYPETRANSWARPMASK) { + if (transwarp_boot_track == 0) { + /* find Transwarp bootfile */ + + int t = dirtrack(type); + int s = (type == IMAGE_D81) ? 3 : 1; + int o = 0; + + do { + int b = linear_sector(type, t, s) * BLOCKSIZE + o; + int filetype = image[b + FILETYPEOFFSET] & 0xf; + if (filetype == FILETYPEDEL) { + continue; + } + + if (is_transwarp_file(image, b)) { + continue; + } + + if (!is_transwarp_bootfile(image, b)) { + continue; + } + + int filetrack = image[b + FILETRACKOFFSET]; + int filesector = image[b + FILESECTOROFFSET]; + if (filetrack == 0) { + continue; + } + + if (verbose) { + printf("\nTranswarp bootfile at T%d/S%d\n", filetrack, filesector); + } + + transwarp_boot_track = filetrack; + transwarp_boot_sector = filesector; + + break; + } while (next_dir_entry(type, image, &t, &s, &o, blockmap)); + } + + if (transwarp_boot_track == 0) { + fprintf(stderr, "ERROR: No Transwarp bootfile provided\n"); + + exit(-10); + } + + int b = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[b + FILETRACKOFFSET] = transwarp_boot_track; + image[b + FILESECTOROFFSET] = transwarp_boot_sector; + } + } + free(blockmap); + + /* update loop files */ + for (int i = 0; i < num_files; i++) { + imagefile *file = files + i; + if (((file->filetype & 0xf) != FILETYPEDEL) && (file->mode & MODE_LOOPFILE)) { + int track, sector, offset; + int index; + if (find_existing_file(type, image, file->plocalname, &index, &track, §or, &offset)) { + /* read track/sector and nrSectors from disk image */ + int b = linear_sector(type, track, sector) * BLOCKSIZE + offset; + file->track = image[b + FILETRACKOFFSET]; + file->sector = image[b + FILESECTOROFFSET]; + file->nrSectors = image[b + FILEBLOCKSLOOFFSET] + (image[b + FILEBLOCKSHIOFFSET] << 8); + + /* update directory entry */ + b = linear_sector(type, dirtrack(type), file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[b + FILETRACKOFFSET] = file->track; + image[b + FILESECTOROFFSET] = file->sector; + + if (file->nrSectorsShown == -1) { + file->nrSectorsShown = file->nrSectors; + } + image[b + FILEBLOCKSLOOFFSET] = file->nrSectorsShown & 255; + image[b + FILEBLOCKSHIOFFSET] = file->nrSectorsShown >> 8; + + if (shadowdirtrack > 0) { + b = linear_sector(type, shadowdirtrack, file->direntrysector) * BLOCKSIZE + file->direntryoffset; + image[b + FILETRACKOFFSET] = file->track; + image[b + FILESECTOROFFSET] = file->sector; + + image[b + FILEBLOCKSLOOFFSET] = file->nrSectors & 255; + image[b + FILEBLOCKSHIOFFSET] = file->nrSectors >> 8; + } + + for (int j = 0; j < num_files; j++) { + imagefile *other_file = files + j; + if ((i != j) + && (file->track == other_file->track) + && (file->sector == other_file->sector)) { + file->sectorInterleave = other_file->sectorInterleave; + + break; + } + } + + continue; + } else { + fprintf(stderr, "ERROR: Loop source file '%s' (%d) not found\n", file->alocalname, i + 1); + exit(-1); + } + } + } +} + +/* Writes 16 bit value to file */ +static size_t +write16(unsigned int value, FILE* f) +{ + char byte = value & 0xff; + size_t bytes_written = fwrite(&byte, 1, 1, f); + + byte = (value >> 8) & 0xff; + bytes_written += fwrite(&byte, 1, 1, f); + + return bytes_written; +} + +/* Writes 32 bit value to file */ +static size_t +write32(unsigned int value, FILE* f) +{ + size_t bytes_written = write16(value, f); + bytes_written += write16(value >> 16, f); + + return bytes_written; +} + +/* Writes image as G64 file */ +static int +generate_uniformat_g64(unsigned char* image, const char *imagepath) +{ + FILE* f = fopen(imagepath, "wb"); + + size_t filepos = 0; + + static const char signature[] = "GCR-1541"; + filepos += fwrite(signature, 1, sizeof signature - 1, f); + + const char version = 0; + filepos += fwrite(&version, 1, 1, f); + + const char num_tracks = 35 * 2; + filepos += fwrite(&num_tracks, 1, 1, f); + + const unsigned int track_size = 7692; /* = track_bytes on tracks 1..17 */ + filepos += write16(track_size, f); + + const unsigned int table_size = num_tracks * 4; + const unsigned int tracks_offset = (int)filepos + (table_size * 2); + + for (int track = 0; track < num_tracks; ++track) { + unsigned int track_offset = 0; + + if ((track & 1) == 0) { + track_offset = tracks_offset + ((track >> 1) * (2 + track_size)); + } + + filepos += write32(track_offset, f); + } + + for (int track = 0; track < num_tracks; ++track) { + unsigned int bit_rate = 0; + + if ((track & 1) == 0) { + switch (sectors_per_track[track >> 1]) { + case 21: + bit_rate = 3; + break; + case 19: + bit_rate = 2; + break; + case 18: + bit_rate = 1; + break; + case 17: + bit_rate = 0; + break; + } + } + + filepos += write32(bit_rate, f); + } + + const unsigned char sync[] = { 0xff, 0xff, 0xff, 0xff, 0xff }; + const char gap[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; + char header_gcr[10]; + + const unsigned int block_size = + (sizeof sync) + + (sizeof header_gcr) + + (sizeof gap) + + (sizeof sync) + + 325; /* data */ + + const char id[3] = { '2', 'A', '\0' }; + + bool is_uniform = true; + + for (int track = 0; track < (num_tracks >> 1); ++track) { + int track_bytes = 0; + int num_sectors = sectors_per_track[track]; + switch (num_sectors) { + case 21: + track_bytes = 7692; + break; /* = track_size */ + case 19: + track_bytes = 7142; + break; + case 18: + track_bytes = 6666; + break; + case 17: + track_bytes = 6250; + break; + } + + filepos += write16(track_bytes, f); + size_t track_begin = filepos; + + int data_bytes = num_sectors * block_size; + int gap_size = (track_bytes - data_bytes) / num_sectors; + if (gap_size < 0) { + printf("\nERROR: Track too small for G64 output\n"); + fclose(f); + + return -1; + } + + float average_gap_remainder = (((float) (track_bytes - data_bytes)) / num_sectors) - gap_size; + if (average_gap_remainder >= 1.0f) { + average_gap_remainder = 0.0f; /* 0..1 */ + } + + float remainder = 0.0f; + for (int sector = 0; sector < num_sectors; ++sector) { + unsigned int gap_bytes = gap_size; + remainder += average_gap_remainder; + if (remainder >= 0.5f) { + remainder -= 1.0f; + ++gap_bytes; + } + + filepos += fwrite(sync, 1, sizeof sync, f); + char header[8] = { + 0x08, /* header ID */ + (char) (sector ^ (track + 1) ^ id[1] ^ id[0]), /* checksum */ + (char) sector, + (char) (track + 1), + id[1], + id[0], + 0x0f, 0x0f + }; + + encode_4_bytes_gcr(header, header_gcr); + encode_4_bytes_gcr(header + 4, header_gcr + 5); + + filepos += fwrite(header_gcr, 1, sizeof header_gcr, f); + filepos += fwrite(gap, 1, sizeof gap, f); + + filepos += fwrite(sync, 1, sizeof sync, f); + + char group[5]; + + char checksum = image[0] ^ image[1] ^ image[2]; + char data[4] = { 0x07, (char) image[0], (char) image[1], (char) image[2] }; + encode_4_bytes_gcr(data, group); + filepos += fwrite(group, 1, sizeof group, f); + for (int i = 0; i < 0x3f; ++i) { + data[0] = image[(i * 4) + 3]; + data[1] = image[(i * 4) + 4]; + data[2] = image[(i * 4) + 5]; + data[3] = image[(i * 4) + 6]; + encode_4_bytes_gcr(data, group); + filepos += fwrite(group, 1, sizeof group, f); + checksum ^= (data[0] ^ data[1] ^ data[2] ^ data[3]); + } + data[0] = image[0xff]; + data[1] = data[0] ^ checksum; + data[2] = 0; + data[3] = 0; + encode_4_bytes_gcr(data, group); + filepos += fwrite(group, 1, sizeof group, f); + + for (int i = gap_bytes; i > 0; --i) { + filepos += fwrite(gap, 1, 1, f); + } + + image += 0x0100; + } /* for each sector */ + + size_t tail_gap = track_bytes - filepos + track_begin; + if (tail_gap > 0) { + for (size_t i = tail_gap; i > 0; --i) { + filepos += fwrite(gap, 1, 1, f); + } + + is_uniform = false; + } + + for (int i = (track_size - track_bytes); i > 0; --i) { + filepos += fwrite(sync, 1, 1, f); + } + } /* for each track */ + + fclose(f); + + if (!is_uniform) { + printf("\nWARNING: \"%s\" is not UniFormAt'ed\n", imagepath); + } + + return 0; +} + +/* Generates a unique filename, either based on the proposed name, or using track and sector. */ +static void +generate_unique_filename(image_type type, unsigned char *image, unsigned char *name, int track, int sector, int start, char marker) +{ + int i, t, s, o; + if(name[0] == 0xa0 || name[0] == 0) { + /* no name provided, create one */ + name[0] = a2p('t'); + pputnum2(name+1, track); + name[3] = a2p('s'); + pputnum2(name+4, sector); + name[6] = a2p('$'); + pputhex(name+7, start); + for(int pos = 11; pos < FILENAMEMAXSIZE; pos++) { + name[pos] = 0xa0; + } + } + int marker_pos = pstrlen(name); + if(marker_pos > FILENAMEMAXSIZE-1) { + marker_pos = FILENAMEMAXSIZE-1; + } + if(marker) { + name[marker_pos] = marker; + } else { + marker_pos++; + } + int appendix = 1; + int appendix_len = 2; + int namelen = pstrlen(name); + while(find_existing_file(type, image, name, &i, &t, &s, &o)) { + marker_pos = namelen + appendix_len; + if(marker_pos > FILENAMEMAXSIZE-1) { + marker_pos = FILENAMEMAXSIZE-1; + if(!marker) { + marker_pos++; + } + } + if(marker) { + name[marker_pos] = marker; + } + int appendix_pos = marker_pos - appendix_len; + name[appendix_pos] = '.'; + pputnum(name + appendix_pos + 1, appendix); + appendix++; + if(appendix == 10) { + appendix_len++; + } + if(appendix == 100) { + appendix_len++; + } + } +} + +/* Count number of blocks in file, assumes valid t/s chain */ +static int +count_blocks(image_type type, unsigned char *image, unsigned int track, int sector) +{ + int num = 0; + while(track != 0) { + int b = linear_sector(type, track, sector); + if(b < 0) { + break; + } + int block_offset = b * BLOCKSIZE; + track = image[block_offset + TRACKLINKOFFSET]; + sector = image[block_offset + SECTORLINKOFFSET]; + num++; + }; + return num; +} + +/* Overwrites all blocks that are marked as potentially allocated with the given value */ +static void +mark_sector_chain(image_type type, unsigned char *image, char* atab, unsigned int track, int sector, unsigned int last_track, int last_sector, int mark) +{ + if(track <= 0 || track > image_num_tracks(type) || sector < 0 || sector > num_sectors(type, track)) { + return; + } + while(1) { + int b = linear_sector(type, track, sector); + atab[b] = mark; + if(track == last_track && sector == last_sector) { + break; + } + int block_offset = b * BLOCKSIZE; + track = image[block_offset + TRACKLINKOFFSET]; + sector = image[block_offset + SECTORLINKOFFSET]; + }; +} + +/* Validates sector chain starting at track/sector, returns how and in which t/s the chain ends */ +static int +validate_sector_chain(image_type type, unsigned char* image, char* atab, unsigned int track, int sector, unsigned int *last_track, int *last_sector) +{ + *last_track = track; + *last_sector = sector; + if(track == 0 || track > image_num_tracks(type)) { + return FIRST_BROKEN; + } + if(sector >= num_sectors(type, track)) { + return FIRST_BROKEN; + } + if(atab[linear_sector(type, track, sector)] != UNALLOCATED) { + return FIRST_BROKEN; + } + while (1) { + atab[linear_sector(type, track, sector)] = POTENTIALLYALLOCATED; + int block_offset = linear_sector(type, track, sector) * BLOCKSIZE; + unsigned int next_track = image[block_offset + TRACKLINKOFFSET]; + int next_sector = image[block_offset + SECTORLINKOFFSET]; + if (next_track == 0) { + return VALID; /* end of valid chain */ + } + if (next_track > image_num_tracks(type)) { + return ILLEGAL_TRACK; + } + if (next_sector >= num_sectors(type, next_track)) { + return ILLEGAL_SECTOR; + } + switch(atab[linear_sector(type, next_track, next_sector)]) { + case POTENTIALLYALLOCATED: + return LOOP; + case ALLOCATED: + return COLLISION; + case FILESTART: + return CHAINED; + case FILESTART_TRUNCATED: + return CHAINED_TRUNCATED; + } + track = next_track; + sector = next_sector; + *last_track = track; + *last_sector = sector; + } +} + +static void +init_atab(image_type type, unsigned char* image, char* atab) +{ + int dt = dirtrack(type); + int ds = (type == IMAGE_D81) ? 3 : 1; + int offset = 0; + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + do { + int db = linear_sector(type, dt, ds); + atab[db] = ALLOCATED; + int filetype = image[db * BLOCKSIZE + offset + FILETYPEOFFSET] & 0xf; + if(filetype != FILETYPEDEL) { + int track = image[db * BLOCKSIZE + offset + FILETRACKOFFSET]; + int sector = image[db * BLOCKSIZE + offset + FILESECTOROFFSET]; + unsigned int last_track; + int last_sector; + int error = validate_sector_chain(type, image, atab, track, sector, &last_track, &last_sector); + if(error != VALID) { + printf("WARNING: file "); + print_filename(stdout, &image[db * BLOCKSIZE + offset + FILENAMEOFFSET]); + printf(" seems corrupt (%s)\n", error_name[error]); + } + mark_sector_chain(type, image, atab, track, sector, last_track, last_sector, ALLOCATED); + } + } while(next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); +} + +/* Write atab into BAM */ +static void +write_atab(image_type type, unsigned char* image, char* atab) +{ + for(unsigned int t = 1; t <= image_num_tracks(type); t++) { + for(int s = 0; s < num_sectors(type, t); s++) { + int b = linear_sector(type, t, s); + int free = (atab[b] == UNALLOCATED); + if(!free) { + /* keep allocated BAM entries, only add new ones */ + mark_sector(type, image, t, s, 0); + } + } + } +} + +/* Try to undelete a file given the directory entry, returns true if successful */ +static bool +undelete_file(image_type type, unsigned char* image, int dt, int ds, int offset, char* atab, int level) +{ + unsigned int last_track; + int last_sector; + char marker = 0; + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE; + int track = image[dirblock + offset + FILETRACKOFFSET]; + int sector = image[dirblock + offset + FILESECTOROFFSET]; + int error = validate_sector_chain(type, image, atab, track, sector, &last_track, &last_sector); + if(error == VALID || ((level == RESTORE_DIR_ONLY || level >= RESTORE_INVALID_FILES) && error != FIRST_BROKEN)) { + unsigned char name[17]; + if(error != VALID) { + if(level != RESTORE_DIR_ONLY) { + /* terminate chain */ + int block_offset = linear_sector(type, last_track, last_sector) * BLOCKSIZE; + image[block_offset + TRACKLINKOFFSET] = 0; + marker = '<'; + } + } + /* restore dir entry */ + memcpy(&name, &image[dirblock + offset + FILENAMEOFFSET], 16); + name[16] = 0; + int b = linear_sector(type, track, sector); + int address = image[b * BLOCKSIZE + 2] + 256 * image[b * BLOCKSIZE + 3]; + generate_unique_filename(type, image, name, track, sector, address, marker); + memcpy(&image[dirblock + offset + FILENAMEOFFSET], &name, 16); + image[dirblock + offset + FILETYPEOFFSET] = 0x82; /* original file type is lost, use closed PRG instead */ + mark_sector_chain(type, image, atab, track, sector, last_track, last_sector, ALLOCATED); + if(level != RESTORE_DIR_ONLY) { + int size = count_blocks(type, image, track, sector); + image[dirblock + offset + FILEBLOCKSHIOFFSET] = size / 256; + image[dirblock + offset + FILEBLOCKSLOOFFSET] = size % 256; + } + return true; + } + if(error != FIRST_BROKEN) { + mark_sector_chain(type, image, atab, track, sector, last_track, last_sector, UNALLOCATED); + } + return false; +} + +/* search for scratched directory entries and restore them */ +static int +undelete(image_type type, unsigned char* image, char* atab, int level) +{ + int dt = dirtrack(type); + int ds = (type == IMAGE_D81) ? 3 : 1; + int nsectors = num_sectors(type, dt); + int offset = 0; + int num_undeleted = 0; + int final_dt, final_ds; /* last linked directory sector and track */ + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + + bool* searched = calloc(nsectors, sizeof(bool)); + if(searched == NULL) { + fprintf(stderr, "ERROR: error allocating memory"); + exit(-1); + } + + /* go through directory sector chain */ + do { + if(dt == dirtrack(type)) { + searched[ds] = true; + } + final_dt = dt; + final_ds = ds; + int db = linear_sector(type, dt, ds); + mark_sector(type, image, dt, ds, 0); /* needs to be updated in BAM, so that dir allocation works correctly later */ + atab[db] = ALLOCATED; + int dirblock = db * BLOCKSIZE; + int filetype = image[dirblock + offset + FILETYPEOFFSET]; + if(filetype == FILETYPEDEL && image[dirblock + offset + FILENAMEOFFSET] != 0 && undelete_file(type, image, dt, ds, offset, atab, level)) { /* filename starting with 0 means that likely there was no file */ + num_undeleted++; + } + } while(next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); + + /* search for unlinked dir sectors */ + dt = dirtrack(type); + for(ds = (type == IMAGE_D81) ? 3 : 1; ds < nsectors; ds++) { + if(!searched[ds]) { + int found = 1; + for(offset = 0; offset < DIRENTRIESPERBLOCK*DIRENTRYSIZE; offset += DIRENTRYSIZE) { + if(!undelete_file(type, image, dt, ds, offset, atab, level)) { + found = 0; + break; + } + } + if(found) { + num_undeleted += 8; + if(!quiet) { + printf("Relinking directory sector %d\n", ds); + } + /* link found dir sector */ + int block_offset = linear_sector(type, final_dt, final_ds) * BLOCKSIZE; + image[block_offset + TRACKLINKOFFSET] = dt; + image[block_offset + SECTORLINKOFFSET] = ds; + /* terminate found dir sector */ + int db = linear_sector(type, dt, ds); + block_offset = db * BLOCKSIZE; + image[block_offset + TRACKLINKOFFSET] = 0; + final_dt = dt; + final_ds = ds; + mark_sector(type, image, dt, ds, 0); /* needs to be updated in BAM, so that dir allocation works correctly later */ + atab[db] = ALLOCATED; + } + } + } + free(searched); + write_atab(type, image, atab); + return num_undeleted; +} + +/* add new DIR entries for wild chains */ +static void +add_wild_to_dir(image_type type, unsigned char* image, char* atab, imagefile files[]) +{ + /* create a DIR entry for each FILESTART */ + for(unsigned int t = 1; t <= image_num_tracks(type); t++) { + for(int s = 0; s < num_sectors(type, t); s++) { + int b = linear_sector(type, t, s); + if(atab[b] == FILESTART || atab[b] == FILESTART_TRUNCATED) { + char marker = atab[b] == FILESTART ? 0 : '<'; + unsigned char name[17]; + name[0] = 0xa0; + int dir_index, dir_sector, dir_offset; + atab[b] = ALLOCATED; + new_dir_slot(type, image, (type == IMAGE_D81 ? 1 : 3), 0, &dir_index, &dir_sector, &dir_offset, files); /* TODO: handle full directory more gracefully */ + int db = linear_sector(type, dirtrack(type), dir_sector); + atab[db] = ALLOCATED; /* make sure that potentially new dir block is marked as used */ + int offset = db * BLOCKSIZE + dir_offset; + image[offset + FILETYPEOFFSET] = 0x82; /* closed PRG */ + image[offset + FILETRACKOFFSET] = t; + image[offset + FILESECTOROFFSET] = s; + image[offset + FILENAMEOFFSET] = 0xa0; /* no proposed filename */ + int address = image[b * BLOCKSIZE + 2] + 256 * image[b * BLOCKSIZE + 3]; + generate_unique_filename(type, image, name, t, s, address, marker); + memcpy(&image[offset + FILENAMEOFFSET], name, 16); + int size = count_blocks(type, image, t, s); + image[offset + FILEBLOCKSHIOFFSET] = size / 256; + image[offset + FILEBLOCKSLOOFFSET] = size % 256; + } + } + } +} + +/* search for wild valid chains of unallocated sectors */ +static int +undelete_wild(image_type type, unsigned char* image, char* atab, int level, imagefile files[]) +{ + int num_undeleted = 0; + int max_bam_sector = (type == IMAGE_D81) ? 2 : 0; + unsigned int dt = dirtrack(type); + /* search for well terminated sector chains */ + for(unsigned int t = 1; t <= image_num_tracks(type); t++) { + for(int s = 0; s < num_sectors(type, t); s++) { + int b = linear_sector(type, t, s); + if(atab[b] == UNALLOCATED && (t != dt || s > max_bam_sector)) { /* ignore bam sectors */ + unsigned int last_track; + int last_sector; + int error = validate_sector_chain(type, image, atab, t, s, &last_track, &last_sector); + int last_block = linear_sector(type, last_track, last_sector); + if(error == CHAINED || error == CHAINED_TRUNCATED) { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, ALLOCATED); + atab[b] = (error == CHAINED) ? FILESTART : CHAINED_TRUNCATED; + int chained_track = image[last_block * BLOCKSIZE + TRACKLINKOFFSET]; + int chained_sector = image[last_block * BLOCKSIZE + SECTORLINKOFFSET]; + int chained_block = linear_sector(type, chained_track, chained_sector); + atab[chained_block] = ALLOCATED; /* overwrite FILESTART */ + } else if(error == VALID) { + if(last_track == t && last_sector == s) { + if(level == RESTORE_INVALID_SINGLES) { + /* single block files should have all 0 after file end */ + bool valid = false; + int last_byte = image[last_block * BLOCKSIZE + SECTORLINKOFFSET]; + if(image[last_block * BLOCKSIZE + TRACKLINKOFFSET] == 0 && last_byte >= 2) { + valid = true; + for(int i = last_byte + 1; i <= 255; i++) { + if(image[last_block * BLOCKSIZE + i] != 0) { + valid = false; + break; + } + } + } + if(valid) { + atab[b] = FILESTART; + num_undeleted++; + } else { + atab[b] = UNALLOCATED; + } + atab[b] = valid ? FILESTART : UNALLOCATED; + } else { + atab[b] = UNALLOCATED; + } + } else { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, ALLOCATED); + atab[b] = FILESTART; + image[last_block * BLOCKSIZE + TRACKLINKOFFSET] = 0; + num_undeleted++; + } + } else { + if(error != FIRST_BROKEN) { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, UNALLOCATED); + } + } + } + } + } + if(num_undeleted) { + write_atab(type, image, atab); + add_wild_to_dir(type, image, atab, files); + } + return num_undeleted; +} + +/* search for wild invalid chains of unallocated sectors and fix them */ +static int +undelete_fix_wild(image_type type, unsigned char* image, char* atab, imagefile files[]) +{ + int num_undeleted = 0; + int max_bam_sector = (type == IMAGE_D81) ? 2 : 0; + unsigned int dt = dirtrack(type); + /* search for broken sector chains */ + for(unsigned int t = 1; t <= image_num_tracks(type); t++) { + for(int s = 0; s < num_sectors(type, t); s++) { + int b = linear_sector(type, t, s); + if(atab[b] == UNALLOCATED && (t != dt || s > max_bam_sector)) { /* ignore bam sectors */ + unsigned int last_track; + int last_sector; + int error = validate_sector_chain(type, image, atab, t, s, &last_track, &last_sector); + int last_block = linear_sector(type, last_track, last_sector); + if(error == CHAINED || error == CHAINED_TRUNCATED) { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, ALLOCATED); + atab[b] = (error == CHAINED) ? FILESTART : FILESTART_TRUNCATED; + int chained_track = image[last_block * BLOCKSIZE + TRACKLINKOFFSET]; + int chained_sector = image[last_block * BLOCKSIZE + SECTORLINKOFFSET]; + int chained_block = linear_sector(type, chained_track, chained_sector); + atab[chained_block] = ALLOCATED; /* overwrite FILESTART */ + } else if(t != last_track || s != last_sector) { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, ALLOCATED); + atab[b] = FILESTART_TRUNCATED; + image[last_block * BLOCKSIZE + TRACKLINKOFFSET] = 0; + image[last_block * BLOCKSIZE + SECTORLINKOFFSET] = 255; + num_undeleted++; + } else { + if(error != FIRST_BROKEN) { + mark_sector_chain(type, image, atab, t, s, last_track, last_sector, UNALLOCATED); + } + } + } + } + } + if(num_undeleted) { + write_atab(type, image, atab); + add_wild_to_dir(type, image, atab, files); + } + return num_undeleted; +} + +/* Tries to restore any deleted or formatted files */ +static void +restore(image_type type, unsigned char* image, int level, imagefile files[]) +{ + int num_undeleted = 0; + /* create block allocation table */ + char *atab = (char *)calloc(image_num_blocks(type), sizeof(char)); + if (atab == NULL) { + fprintf(stderr, "ERROR: error allocating memory"); + exit(-1); + } + init_atab(type, image, atab); + if(level == RESTORE_DIR_ONLY) { + num_undeleted += undelete(type, image, atab, RESTORE_DIR_ONLY); + } else { + num_undeleted += undelete(type, image, atab, RESTORE_VALID_FILES); + if(level >= RESTORE_VALID_CHAINS) { + num_undeleted += undelete_wild(type, image, atab, RESTORE_VALID_CHAINS, files); + } + if(level >= RESTORE_INVALID_FILES) { + num_undeleted += undelete(type, image, atab, RESTORE_INVALID_FILES); + } + if(level >= RESTORE_INVALID_CHAINS) { + num_undeleted += undelete_fix_wild(type, image, atab, files); + } + if(level >= RESTORE_INVALID_SINGLES) { + num_undeleted += undelete_wild(type, image, atab, RESTORE_INVALID_SINGLES, files); + } + } + free(atab); + if(num_undeleted) { + modified = 1; + } + if(!quiet) { + printf("%d files undeleted", num_undeleted); + if(num_undeleted) { + printf(", '<' at filename end marks truncated files"); + } + printf("\n"); + } +} + +/* Prints a command line to create dir art like the given image */ +static void +convert_to_commandline(image_type type, unsigned char* image) +{ + char *blockmap = calloc(image_num_blocks(type), sizeof(char)); + if(blockmap == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + exit(-1); + } + printf("\nCommandline to create directory art: -m -n \""); + unsigned int bam = linear_sector(type, dirtrack(type), 0) * BLOCKSIZE; + print_filename_with_escapes(image + bam + get_header_offset(type), FILENAMEMAXSIZE); + printf("\" -i \""); + print_filename_with_escapes(image + bam + get_id_offset(type), 5); + printf("\" "); + + int ds = (type == IMAGE_D81) ? 3 : 1; + int dt = dirtrack(type); + int offset = 0; + do { + int dirblock = linear_sector(type, dt, ds) * BLOCKSIZE + offset; + int filetype = image[dirblock + FILETYPEOFFSET]; + if (filetype) { + if(filetype != 0x82) { + printf("-T %d ", filetype); + } + int size = image[dirblock + FILEBLOCKSLOOFFSET] + 256 * image[dirblock + FILEBLOCKSHIOFFSET]; + if(size != 0) { + printf("-B %d ", size); + } + unsigned char *filename = (unsigned char *) image + dirblock + FILENAMEOFFSET; + printf("-N -f \""); + print_filename_with_escapes(filename, FILENAMEMAXSIZE); + printf("\" -L "); + } + } while (next_dir_entry(type, image, &dt, &ds, &offset, blockmap)); + free(blockmap); + printf("\n\n"); +} + +/* Performs strict CBM DOS validation on the image */ +static void +validate(image_type type, unsigned char* image) +{ + /* create block allocation table */ + char *atab = (char *)calloc(image_num_blocks(type), sizeof(int)); + if (atab == NULL) { + fprintf(stderr, "ERROR: error allocating memory"); + exit(-1); + } + /* check format specifier */ + int format = image[linear_sector(type, dirtrack(type), 0) * BLOCKSIZE + 2]; + if (format != 0x41) { + fprintf(stderr, "ERROR: validation failed, format specifier in directory (0x%02x) does not specify 1541 (0x41)\n", format); + exit(-1); + } + /* check each directory entry and set block allocation table */ + atab[linear_sector(type, dirtrack(type), 0)] = ALLOCATED; + unsigned int dt = dirtrack(type); + int dirsector = 1; + unsigned int start_track = 1; + while (start_track != 0) { + atab[linear_sector(type, dt, dirsector)] = ALLOCATED; + int dirblock = linear_sector(type, dt, dirsector) * BLOCKSIZE; + for (int direntry = 0; direntry < DIRENTRIESPERBLOCK; direntry++) { + int entryOffset = direntry * DIRENTRYSIZE; + int filetype = image[dirblock + entryOffset + FILETYPEOFFSET] & 0xf; + if (filetype > 4) { + fprintf(stderr, "ERROR: validation failed, illegal file type (0x%02x) in directory\n", filetype); + exit(-1); + } + if (filetype != 0) { /* skip deleted entries */ + start_track = image[dirblock + entryOffset + FILETRACKOFFSET]; + int start_sector = image[dirblock + entryOffset + FILESECTOROFFSET]; + if (start_track == 0 || start_track > image_num_tracks(type)) { + fprintf(stderr, "ERROR: validation failed, illegal track reference (%u) in directory\n", start_track); + exit(-1); + } + if (start_sector >= num_sectors(type, start_track)) { + fprintf(stderr, "ERROR: validation failed, illegal sector reference (track %u, sector %d) in directory\n", start_track, start_sector); + exit(-1); + } + if (atab[linear_sector(type, start_track, start_sector)] == ALLOCATED) { + fprintf(stderr, "ERROR: validation failed, file starts in the middle of another file (track %u, sector %d)\n", start_track, start_sector); + exit(-1); + } + if (atab[linear_sector(type, start_track, start_sector)] != FILESTART) { /* loop files are allowed */ + unsigned int error_track; + int error_sector; + int result = validate_sector_chain(type, image, atab, start_track, start_sector, &error_track, &error_sector); + switch(result) { + case ILLEGAL_TRACK: + fprintf(stderr, "ERROR: validation failed, illegal track reference in file sector chain at track %d, sector %d\n", error_track, error_sector); + exit(-1); + case ILLEGAL_SECTOR: + fprintf(stderr, "ERROR: validation failed, illegal sector reference in file sector chain at track %d, sector %d\n", error_track, error_sector); + exit(-1); + case LOOP: + fprintf(stderr, "ERROR: validation failed, loop in file sector chain at track %d, sector %d\n", error_track, error_sector); + exit(-1); + case COLLISION: + case CHAINED: + fprintf(stderr, "ERROR: validation failed, collision with existing file in file sector chain at track %d, sector %d\n", error_track, error_sector); + exit(-1); + } + atab[linear_sector(type, start_track, start_sector)] = FILESTART; + } + } + } + dt = image[dirblock + TRACKLINKOFFSET]; + dirsector = image[dirblock + SECTORLINKOFFSET]; + if (dt == 0) { + break; + } + } + /* check BAM for consistency with block allocation table */ + for (unsigned int t = 1; t <= image_num_tracks(type); t++) { + unsigned char* bitmap = image + get_bam_offset(type, t); + int num_free = 0; + for (int s = 0; s < num_sectors(type, t); s++) { + int atab_used = (atab[linear_sector(type, t, s)] != UNALLOCATED); + int bam_used = ((bitmap[s >> 3] & (1 << (s & 7))) == 0); + num_free += (1 - bam_used); + if (bam_used != atab_used) { + fprintf(stderr, "ERROR: validation failed, BAM (%s) is not consistent with files (%s) for track %u sector %d\n", bam_used ? "used" : "free", atab_used ? "used" : "free", t, s); + exit(-1); + } + } + if (*(bitmap - 1) != num_free) { + fprintf(stderr, "ERROR: validation failed, BAM number of free blocks (%d) is not consistent with bitmap (%#02x%#02x%#02x) for track %u\n", *(bitmap - 1), *bitmap, *(bitmap + 1), *(bitmap + 2), t); + exit(-1); + } + } + free(atab); + if(!quiet) { + fprintf(stderr, "CBM DOS validation passed\n"); + } +} + +int +main(int argc, char* argv[]) +{ + imagefile files[MAXNUMFILES_D81]; + memset(files, 0, sizeof files); + + image_type type = IMAGE_D64; + char* imagepath = NULL; + char* filename_g64 = NULL; + unsigned char* header = (unsigned char*)"cc1541"; + unsigned char* id = (unsigned char*)"00 2a"; + unsigned char* bam_message = NULL; + int dirtracksplit = 1; + int usedirtrack = 0; + unsigned int shadowdirtrack = 0; + + int default_first_sector_new_track = 0; + int first_sector_new_track = 0; + int defaultSectorInterleave = 10; + int sectorInterleave = 0; + int dir_sector_interleave = 3; + int numdirblocks = 2; + int nrSectorsShown = -1; + unsigned char* filename = NULL; + int set_header = 0; + int nooverwrite = 0; + int dovalidate = 0; + int restore_level = -1; + int ignore_collision = 0; + int filetype = 0x82; /* default is closed PRG */ + bool filetype_set = false; + bool print_art_commandline = false; + + /* flags to detect illegal settings for Transwarp or D81 */ + int transwarp_set = 0; + int sector_interleave_set = 0; + int default_sector_interleave_set = 0; + int file_start_sector_set = 0; + int new_track_start_sector_set = 0; + + int retval = 0; + + int i, j; + + if (argc == 1 || strcmp(argv[argc-1], "-h") == 0) { + usage(); + } + for (j = 1; j < argc - 1; j++) { + if (strcmp(argv[j], "-n") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -n\n"); + return -1; + } + header = (unsigned char*)argv[++j]; + set_header = 1; + modified = 1; + } else if (strcmp(argv[j], "-i") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -i\n"); + return -1; + } + id = (unsigned char*)argv[++j]; + set_header = 1; + modified = 1; + } else if (strcmp(argv[j], "-H") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -H\n"); + return -1; + } + bam_message = (unsigned char*)argv[++j]; + set_header = 1; + modified = 1; + } else if (strcmp(argv[j], "-M") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &max_hash_length)) { + fprintf(stderr, "ERROR: Error parsing argument for -M\n"); + return -1; + } + if ((max_hash_length < 1) || (max_hash_length > FILENAMEMAXSIZE)) { + fprintf(stderr, "ERROR: Hash computation maximum filename length %d specified\n", max_hash_length); + return -1; + } + } else if (strcmp(argv[j], "-m") == 0) { + ignore_collision = 1; + } else if (strcmp(argv[j], "-F") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &first_sector_new_track)) { + fprintf(stderr, "ERROR: Error parsing argument for -F\n"); + return -1; + } + new_track_start_sector_set = 1; + } else if (strcmp(argv[j], "-S") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &defaultSectorInterleave)) { + fprintf(stderr, "ERROR: Error parsing argument for -S\n"); + return -1; + } + if(defaultSectorInterleave < 1 || defaultSectorInterleave > 21) { + fprintf(stderr, "ERROR: Illegal value for -S\n"); + return -1; + } + default_sector_interleave_set = 1; + } else if (strcmp(argv[j], "-s") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", §orInterleave)) { + fprintf(stderr, "ERROR: Error parsing argument for -s\n"); + return -1; + } + if(sectorInterleave < 1 || sectorInterleave > 21) { + fprintf(stderr, "ERROR: Illegal value for -s\n"); + return -1; + } + sector_interleave_set = 1; + } else if (strcmp(argv[j], "-f") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -f\n"); + return -1; + } + filename = (unsigned char*)argv[++j]; + } else if (strcmp(argv[j], "-e") == 0) { + files[num_files].mode |= MODE_SAVETOEMPTYTRACKS; + } else if (strcmp(argv[j], "-E") == 0) { + files[num_files].mode |= MODE_FITONSINGLETRACK; + } else if (strcmp(argv[j], "-r") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &i)) { + fprintf(stderr, "ERROR: Error parsing argument for -r\n"); + return -1; + } + if ((i < 1) || (((i << MODE_MIN_TRACK_SHIFT) & MODE_MIN_TRACK_MASK) != (i << MODE_MIN_TRACK_SHIFT))) { + fprintf(stderr, "ERROR: Invalid minimum track %d specified\n", i); + return -1; + } + files[num_files].mode = (files[num_files].mode & ~MODE_MIN_TRACK_MASK) | (i << MODE_MIN_TRACK_SHIFT); + } else if (strcmp(argv[j], "-b") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &i)) { + fprintf(stderr, "ERROR: Error parsing argument for -b\n"); + return -1; + } + if ((i < 0) || (i >= num_sectors(type, 1))) { + fprintf(stderr, "ERROR: Invalid beginning sector %d specified\n", i); + return -1; + } + files[num_files].mode = (files[num_files].mode & ~MODE_BEGINNING_SECTOR_MASK) | (i + 1); + file_start_sector_set = 1; + } else if (strcmp(argv[j], "-c") == 0) { + files[num_files].mode |= MODE_SAVECLUSTEROPTIMIZED; + } else if (strcmp(argv[j], "-o") == 0) { + nooverwrite = 1; + } else if (strcmp(argv[j], "-V") == 0) { + dovalidate = 1; + } else if (strcmp(argv[j], "-T") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -T\n"); + return -1; + } + if (strcmp(argv[j + 1], "DEL") == 0) { + filetype = (filetype & 0xf0) | FILETYPEDEL; + } else if (strcmp(argv[j + 1], "SEQ") == 0) { + filetype = (filetype & 0xf0) | FILETYPESEQ; + } else if (strcmp(argv[j + 1], "PRG") == 0) { + filetype = (filetype & 0xf0) | FILETYPEPRG; + } else if (strcmp(argv[j + 1], "USR") == 0) { + filetype = (filetype & 0xf0) | FILETYPEUSR; + } else if (strcmp(argv[j + 1], "REL") == 0) { + filetype = (filetype & 0xf0) | FILETYPEREL; + } else { + char* dummy; + filetype = strtol(argv[j + 1], &dummy, 10); + if(dummy == argv[j + 1] || *dummy != 0 || filetype < 0 || filetype > 255) { + fprintf(stderr, "ERROR: Error parsing argument for -T\n"); + return -1; + } + } + filetype_set = true; + j++; + } else if (strcmp(argv[j], "-O") == 0) { + filetype &= 0x7f; + } else if (strcmp(argv[j], "-P") == 0) { + filetype |= 0x40; + } else if (strcmp(argv[j], "-N") == 0) { + files[num_files].force_new = 1; + } else if (strcmp(argv[j], "-K") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -K\n"); + return -1; + } + evalhexescape((unsigned char *) argv[++j], files[num_files].key, TRANSWARPKEYSIZE, 0); + files[num_files].have_key = true; + } else if ((strcmp(argv[j], "-w") == 0) + || (strcmp(argv[j], "-W") == 0)) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for %s\n", argv[j]); + return -1; + } + files[num_files].alocalname = (unsigned char*)argv[j + 1]; + if (filename == NULL) { + ascii2petscii(basename(files[num_files].alocalname), files[num_files].pfilename, FILENAMEMAXSIZE); /* do not eval escapes when converting the filename, as the local filename could contain the escape char */ + } else { + evalhexescape(filename, files[num_files].pfilename, FILENAMEMAXSIZE, FILENAMEEMPTYCHAR); + } + files[num_files].sectorInterleave = sectorInterleave ? sectorInterleave : defaultSectorInterleave; + files[num_files].first_sector_new_track = first_sector_new_track; + files[num_files].nrSectorsShown = nrSectorsShown; + files[num_files].filetype = filetype; + files[num_files].direntryindex = -1; + + if (strcmp(argv[j], "-W") == 0) { + if(nrSectorsShown != -1) { + fprintf(stderr, "ERROR: -B cannot be used for Transwarp files\n"); + return -1; + } + transwarp_set = true; + if(!filetype_set && files[num_files].have_key) { + files[num_files].filetype = (filetype & 0xf0) | FILETYPEUSR | FILETYPETRANSWARPMASK; + } else { + files[num_files].filetype = filetype | FILETYPETRANSWARPMASK; + } + files[num_files].sectorInterleave = 1; + } + + first_sector_new_track = default_first_sector_new_track; + filename = NULL; + sectorInterleave = 0; + nrSectorsShown = -1; + filetype = 0x82; + filetype_set = false; + num_files++; + modified = 1; + j++; + } else if (strcmp(argv[j], "-l") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -l\n"); + return -1; + } + files[num_files].alocalname = (unsigned char*)argv[j + 1]; + evalhexescape(files[num_files].alocalname, files[num_files].plocalname, FILENAMEMAXSIZE, FILENAMEEMPTYCHAR); + if (filename == NULL) { + fprintf(stderr, "ERROR: Loop files require a filename set with -f\n"); + return -1; + } + evalhexescape(filename, files[num_files].pfilename, FILENAMEMAXSIZE, FILENAMEEMPTYCHAR); + if(memcmp(files[num_files].pfilename, files[num_files].plocalname, FILENAMEMAXSIZE) == 0 && !files[num_files].force_new) { + fprintf(stderr, "ERROR: Loop file cannot have the same name as the file they refer to, unless with -N\n"); + return -1; + } + files[num_files].mode |= MODE_LOOPFILE; + files[num_files].sectorInterleave = 0; + files[num_files].first_sector_new_track = first_sector_new_track; + first_sector_new_track = default_first_sector_new_track; + files[num_files].nrSectorsShown = nrSectorsShown; + files[num_files].filetype = filetype; + files[num_files].direntryindex = -1; + filename = NULL; + sectorInterleave = 0; + nrSectorsShown = -1; + filetype = 0x82; + filetype_set = false; + num_files++; + modified = 1; + j++; + } else if (strcmp(argv[j], "-L") == 0) { + if (filename == NULL) { + fprintf(stderr, "ERROR: Writing no file using -L requires disk filename set with -f\n"); + return -1; + } + evalhexescape(filename, files[num_files].pfilename, FILENAMEMAXSIZE, FILENAMEEMPTYCHAR); + files[num_files].nrSectorsShown = nrSectorsShown; + files[num_files].filetype = filetype; + files[num_files].direntryindex = -1; + files[num_files].mode |= MODE_NOFILE; + + first_sector_new_track = default_first_sector_new_track; + filename = NULL; + sectorInterleave = 0; + nrSectorsShown = -1; + filetype = 0x82; + filetype_set = false; + num_files++; + modified = 1; + } else if (strcmp(argv[j], "-x") == 0) { + dirtracksplit = 0; + } else if (strcmp(argv[j], "-t") == 0) { + usedirtrack = 1; + } else if (strcmp(argv[j], "-d") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%u", &shadowdirtrack)) { + fprintf(stderr, "ERROR: Error parsing argument for -d\n"); + return -1; + } + modified = 1; + } else if (strcmp(argv[j], "-u") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &numdirblocks)) { + fprintf(stderr, "ERROR: Error parsing argument for -u\n"); + return -1; + } + } else if (strcmp(argv[j], "-B") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &nrSectorsShown)) { + fprintf(stderr, "ERROR: Error parsing argument for -B\n"); + return -1; + } + if (nrSectorsShown < 0 || nrSectorsShown > 65535) { + fprintf(stderr, "ERROR: Argument must be between 0 and 65535 for -B\n"); + return -1; + } + } else if (strcmp(argv[j], "-4") == 0) { + type = IMAGE_D64_EXTENDED_SPEED_DOS; + modified = 1; + } else if (strcmp(argv[j], "-R") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &restore_level)) { + fprintf(stderr, "ERROR: Error parsing argument for -R\n"); + return -1; + } + if(restore_level < 0 || restore_level > 5) { + fprintf(stderr, "ERROR: Argument must be between 0 and 5 for -R\n"); + return -1; + } + } else if (strcmp(argv[j], "-5") == 0) { + type = IMAGE_D64_EXTENDED_DOLPHIN_DOS; + modified = 1; + } else if (strcmp(argv[j], "-a") == 0) { + print_art_commandline = true; + } else if(strcmp(argv[j], "-g") == 0) { + if (argc < j + 2) { + fprintf(stderr, "ERROR: Error parsing argument for -g\n"); + return -1; + } + filename_g64 = argv[++j]; + } else if (strcmp(argv[j], "-U") == 0) { + if ((argc < j + 2) || !sscanf(argv[++j], "%d", &unicode)) { + fprintf(stderr, "ERROR: Error parsing argument for -U\n"); + return -1; + } + if(unicode < 0 || unicode > 2) { + fprintf(stderr, "ERROR: Argument must be between 0 and 2 for -U\n"); + return -1; + } + } else if (strcmp(argv[j], "-q") == 0) { + quiet = 1; + } else if (strcmp(argv[j], "-v") == 0) { + verbose = 1; + } else if (strcmp(argv[j], "-h") == 0) { + usage(); + } else { + fprintf(stderr, "ERROR: Error parsing command line at \"%s\"\n", argv[j]); + printf("Use -h for help.\n"); + return -1; + } + } + if (j >= argc) { + fprintf(stderr, "ERROR: No image file provided, or misparsed last option\n"); + return -1; + } + imagepath = argv[argc-1]; + + if (strlen(imagepath) >= 4) { + if (strcmp(imagepath + strlen(imagepath) - 4, ".d71") == 0) { + if ((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) { + fprintf(stderr, "ERROR: Extended .d71 images are not supported\n"); + return -1; + } + type = IMAGE_D71; + } else if (strcmp(imagepath + strlen(imagepath) - 4, ".d81") == 0) { + if ((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) { + fprintf(stderr, "ERROR: Extended .d81 images are not supported\n"); + return -1; + } + type = IMAGE_D81; + dir_sector_interleave = 1; + } + } + + if(bam_message != NULL && type != IMAGE_D64 && type != IMAGE_D64_EXTENDED_SPEED_DOS) { + fprintf(stderr, "ERROR: Bam message only supported for D64 and SPEED DOS images\n"); + return -1; + } + + if(shadowdirtrack > image_num_tracks(type) || (int)shadowdirtrack == dirtrack(type) || (type == IMAGE_D71 && (int)shadowdirtrack == dirtrack(type) + D64NUMTRACKS)) { + fprintf(stderr, "ERROR: Invalid shadow directory track\n"); + return -1; + } + + if (type != IMAGE_D64) { + if (transwarp_set + && (type != IMAGE_D64_EXTENDED_SPEED_DOS) + && (type != IMAGE_D64_EXTENDED_DOLPHIN_DOS)) { + fprintf(stderr, "ERROR: Transwarp encoding is not supported for non-D64 images\n"); + return -1; + } + + if (filename_g64 != NULL) { + fprintf(stderr, "ERROR: G64 output is only supported for non-extended D64 images\n"); + return -1; + } + } + + /* Check for unsupported settings for D81 */ + if (type == IMAGE_D81) { + if (default_sector_interleave_set) { + fprintf(stderr, "ERROR: -S is not supported for D81 images\n"); + return -1; + } + if (sector_interleave_set) { + fprintf(stderr, "ERROR: -s is not supported for D81 images\n"); + return -1; + } + if (new_track_start_sector_set) { + fprintf(stderr, "ERROR: -F is not supported for D81 images\n"); + return -1; + } + if (file_start_sector_set) { + fprintf(stderr, "ERROR: -b is not supported for D81 images\n"); + return -1; + } + } + + /* Change locale from C to default to allow unicode printouts */ + if(unicode != 0) { + setlocale(LC_ALL, ""); + } + + /* quiet has precedence over verbose */ + if(quiet) { + verbose = 0; + } + + /* open image */ + unsigned int imagesize = image_size(type); + unsigned char* image = (unsigned char*)calloc(imagesize, sizeof(unsigned char)); + if (image == NULL) { + fprintf(stderr, "ERROR: Memory allocation error\n"); + return -1; + } + FILE* f = fopen(imagepath, "rb"); + if (f == NULL) { + modified = 1; + if (!quiet) { + printf("Adding %d files to new image %s\n", num_files, basename((unsigned char*)imagepath)); + } + initialize_directory(type, image, header, id, bam_message, shadowdirtrack); + } else { + if (!quiet) { + printf("Adding %d files to existing image %s\n", num_files, basename((unsigned char*)imagepath)); + } + size_t read_size = fread(image, 1, imagesize, f); + fclose(f); + if (read_size != imagesize) { + if (((type == IMAGE_D64_EXTENDED_SPEED_DOS) || (type == IMAGE_D64_EXTENDED_DOLPHIN_DOS)) && (read_size == D64SIZE)) { + /* Clear extra tracks */ + memset(image + image_size(IMAGE_D64), 0, image_size(type) - image_size(IMAGE_D64)); + + /* Mark all extra sectors unused */ + for (unsigned int t = D64NUMTRACKS + 1; t <= image_num_tracks(type); t++) { + for (int s = 0; s < num_sectors(type, t); s++) { + mark_sector(type, image, t, s, 1 /* free */); + } + } + } else { + fprintf(stderr, "ERROR: Wrong filesize: expected to read %u bytes, but read %u bytes\n", imagesize, (unsigned int) read_size); + return -1; + } + } + if (dovalidate) { + validate(type, image); + } + if (restore_level >= 0) { + restore(type, image, restore_level, files); + } + if (set_header) { + update_directory(type, image, header, id, bam_message, shadowdirtrack); + } + } + + /* Print command line before adding anything to the image */ + if(print_art_commandline) { + convert_to_commandline(type, image); + } + + /* Create directory entries */ + create_dir_entries(type, image, files, num_files, dir_sector_interleave, shadowdirtrack, nooverwrite); + + /* Write files and mark sectors in BAM */ + write_files(type, image, files, num_files, usedirtrack, dirtracksplit, shadowdirtrack, numdirblocks, dir_sector_interleave); + + /* Print allocation info */ + if (verbose) { + print_file_allocation(type, image, files, num_files); + } + int blocks_free = check_bam(type, image); + + /* Print directory */ + if (!quiet) { + print_directory(type, image, blocks_free); + } + + /* Show directory issues if present */ + if(dir_error != DIR_OK) { + fprintf(stdout, "WARNING: %s\n", dir_error_string[dir_error]); + } + + /* Save image */ + if(modified) { + f = fopen(imagepath, "wb"); + if (f == NULL || fwrite(image, imagesize, 1, f) != 1) { + fprintf(stderr, "ERROR: Failed to write %s\n", imagepath); + retval = -1; + } + if (f != NULL) { + fclose(f); + } + } + + /* Save optional g64 image */ + if (filename_g64 != NULL) { + /* retval might be set to -1 already. Thus we need to take its + previous state and OR it with the following return value. */ + retval |= generate_uniformat_g64(image, filename_g64); + } + + if (!ignore_collision && check_hashes(type, image)) { + fprintf(stderr, "\nERROR: Filename hash collision detected, image is not compatible with Krill's loader. Use -m to ignore this error.\n"); + retval = -1; + } + + free(image); + + return retval; +} diff --git a/loader/tools/cc1541/cc1541.sln b/loader/tools/cc1541/cc1541.sln new file mode 100644 index 0000000..4efb95b --- /dev/null +++ b/loader/tools/cc1541/cc1541.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cc1541", "cc1541.vcxproj", "{230D43EC-B2E8-447B-8605-D166B69FD94B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cc1541", "test_cc1541.vcxproj", "{4206D44B-8628-4E98-9F6F-775C8AC5E1B9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Debug|x64.ActiveCfg = Debug|x64 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Debug|x64.Build.0 = Debug|x64 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Debug|x86.ActiveCfg = Debug|Win32 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Debug|x86.Build.0 = Debug|Win32 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Release|x64.ActiveCfg = Release|x64 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Release|x64.Build.0 = Release|x64 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Release|x86.ActiveCfg = Release|Win32 + {230D43EC-B2E8-447B-8605-D166B69FD94B}.Release|x86.Build.0 = Release|Win32 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Debug|x64.ActiveCfg = Debug|x64 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Debug|x64.Build.0 = Debug|x64 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Debug|x86.ActiveCfg = Debug|Win32 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Debug|x86.Build.0 = Debug|Win32 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Release|x64.ActiveCfg = Release|x64 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Release|x64.Build.0 = Release|x64 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Release|x86.ActiveCfg = Release|Win32 + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/loader/tools/cc1541/cc1541.vcxproj b/loader/tools/cc1541/cc1541.vcxproj new file mode 100644 index 0000000..4f0fcd0 --- /dev/null +++ b/loader/tools/cc1541/cc1541.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {230D43EC-B2E8-447B-8605-D166B69FD94B} + Win32Proj + cc1541 + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + $(Platform)\$(ProjectName)\$(Configuration)\ + + + false + + + false + $(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/loader/tools/cc1541/cc1541.vcxproj.filters b/loader/tools/cc1541/cc1541.vcxproj.filters new file mode 100644 index 0000000..88ddd8a --- /dev/null +++ b/loader/tools/cc1541/cc1541.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/loader/tools/cc1541/test_cc1541.c b/loader/tools/cc1541/test_cc1541.c new file mode 100755 index 0000000..60e4671 --- /dev/null +++ b/loader/tools/cc1541/test_cc1541.c @@ -0,0 +1,2020 @@ +/******************************************************************************* +* Copyright (c) 2018-2022 Claus, Björn Esser +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*******************************************************************************/ + +#define _CRT_SECURE_NO_WARNINGS /* avoid security warnings for MSVC */ + +#include +#include +#include +#include +#include + +#define CMD_LINE_LEN 4096 + 1 /* 4k is enough for commandline buffer. */ + +#define MERGE_OUT_ERR " 2>&1" +#ifdef _WIN32 +#define FILESEPARATOR "\\" +#define NULL_DEV "> nul" +#else +#define FILESEPARATOR "/" +#define NULL_DEV "> /dev/null" +#endif + +enum { + NO_ERROR = 0, + ERROR_ALLOCATION, + ERROR_RETURN_VALUE, + ERROR_NO_OUTPUT +}; + +const unsigned int track_offset[] = { /* taken from http://unusedino.de/ec64/technical/formats/d64.html */ + 0x00000, 0x01500, 0x02A00, 0x03F00, 0x05400, 0x06900, 0x07E00, 0x09300, + 0x0A800, 0x0BD00, 0x0D200, 0x0E700, 0x0FC00, 0x11100, 0x12600, 0x13B00, + 0x15000, 0x16500, 0x17800, 0x18B00, 0x19E00, 0x1B100, 0x1C400, 0x1D700, + 0x1EA00, 0x1FC00, 0x20E00, 0x22000, 0x23200, 0x24400, 0x25600, 0x26700, + 0x27800, 0x28900, 0x29A00, 0x2AB00, 0x2BC00, 0x2CD00, 0x2DE00, 0x2EF00 +}; + +const unsigned int track_offset_b[] = { + /* second side of D71 */ + 0x2AB00 + 0x00000, 0x2AB00 + 0x01500, 0x2AB00 + 0x02A00, 0x2AB00 + 0x03F00, 0x2AB00 + 0x05400, 0x2AB00 + 0x06900, 0x2AB00 + 0x07E00, 0x2AB00 + 0x09300, + 0x2AB00 + 0x0A800, 0x2AB00 + 0x0BD00, 0x2AB00 + 0x0D200, 0x2AB00 + 0x0E700, 0x2AB00 + 0x0FC00, 0x2AB00 + 0x11100, 0x2AB00 + 0x12600, 0x2AB00 + 0x13B00, + 0x2AB00 + 0x15000, 0x2AB00 + 0x16500, 0x2AB00 + 0x17800, 0x2AB00 + 0x18B00, 0x2AB00 + 0x19E00, 0x2AB00 + 0x1B100, 0x2AB00 + 0x1C400, 0x2AB00 + 0x1D700, + 0x2AB00 + 0x1EA00, 0x2AB00 + 0x1FC00, 0x2AB00 + 0x20E00, 0x2AB00 + 0x22000, 0x2AB00 + 0x23200, 0x2AB00 + 0x24400, 0x2AB00 + 0x25600, 0x2AB00 + 0x26700, + 0x2AB00 + 0x27800, 0x2AB00 + 0x28900, 0x2AB00 + 0x29A00, 0x2AB00 + 0x2AB00, 0x2AB00 + 0x2BC00, 0x2AB00 + 0x2CD00, 0x2AB00 + 0x2DE00, 0x2AB00 + 0x2EF00 +}; + +/* Runs the binary with the provided commandline and returns the content of the output image file in a buffer */ +int +run_binary(const char* binary, const char* options, const char* image_name, char **image, size_t *size, bool silent) +{ + struct stat st; + static char command_line[CMD_LINE_LEN]; + + if (*image != NULL) { + free(*image); + *image = NULL; + } + + /* build command line */ + if(silent) { + snprintf(command_line, CMD_LINE_LEN, "%s %s %s %s %s", binary, options, image_name, NULL_DEV, MERGE_OUT_ERR); + } else { + snprintf(command_line, CMD_LINE_LEN, "%s %s %s %s", binary, options, image_name, NULL_DEV); + } + + if (system(command_line) != 0) { + return ERROR_RETURN_VALUE; + } + + if (stat(image_name, &st)) { + return ERROR_NO_OUTPUT; + } + + *size = st.st_size; + *image = calloc(st.st_size, sizeof(unsigned char)); + + FILE* f = fopen(image_name, "rb"); + if (f == NULL) { + return ERROR_NO_OUTPUT; + } + if (fread(*image, *size, 1, f) != 1) { + fprintf(stderr, "ERROR: Unexpected filesize when reading %s\n", image_name); + return ERROR_NO_OUTPUT; + } + fclose(f); + + return NO_ERROR; +} + +/* runs the binary with a given command line and image output file, reads the output into a buffer and deletes the file then */ +int +run_binary_cleanup(const char* binary, const char* options, const char* image_name, char **image, size_t *size, bool silent) +{ + int status = run_binary(binary, options, image_name, image, size, silent); + remove(image_name); + return status; +} + +/* writes image to file */ +int +write_file(const char* name, size_t size, char *image) +{ + FILE* f = fopen(name, "wb"); + if(f == NULL) { + fprintf(stderr, "ERROR: Could not create output file %s\n", name); + return ERROR_NO_OUTPUT; + } + for (size_t i = 0; i < size; i++) { + fputc(image[i], f); + } + fclose(f); + return NO_ERROR; +} + +/* creates a file with given name, size and filled with given value */ +int +create_value_file(const char* name, int size, char value) +{ + FILE* f = fopen(name, "wb"); + if (f == NULL) { + fprintf(stderr, "ERROR: Could not create output file %s\n", name); + return ERROR_NO_OUTPUT; + } + for (int i = 0; i < size; i++) { + fputc(value, f); + } + fclose(f); + return NO_ERROR; +} + +/* checks if a given block in the image is filled with a given value */ +int +block_is_filled(char* image, int block, int value) +{ + for (int i = 0; i < 254; i++) { + if (image[block * 256 + 2 + i] != value) { + return 0; + } + } + return 1; +} + +int +main(int argc, char* argv[]) +{ + struct stat st; + const char* binary; + char *image = NULL; + size_t size; + int test = 0; + int passed = 0; + char *description; + int result = 0; + + enum { + TEST_PASS = 0, + TEST_FAIL = 1, + TEST_UNRESOLVED = 2 + }; + const char *const result_str[] = { + "PASS", + "FAIL", + "UNRESOLVED" + }; + const int test_pad = 3; /* Decimal digits of the test counter */ + + if (argc != 2) { + printf("Test suite for cc1541\n"); + printf("Usage: test_cc1541 \n"); + return(-1); + } + if (stat(argv[1], &st)) { + printf("ERROR: Test binary %s does not exist.\n", argv[1]); + return(-1); + } + + binary = argv[1]; + + description = "Size of empty D64 image should be 174848"; + ++test; + if (run_binary_cleanup(binary, "", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 174848) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Size of empty G64 image should be 269862"; + ++test; + if (run_binary_cleanup(binary, "-g image.g64", "image.g64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 269862) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Size of empty D71 image should be 2*174848"; + ++test; + if (run_binary_cleanup(binary, "", "image.d71", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 2 * 174848) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Size of empty Speed DOS D64 image should be 174848+5*17*256"; + ++test; + if (run_binary_cleanup(binary, "-4", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 174848 + 5 * 17 * 256) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Size of empty Dolphin DOS D64 image should be 174848+5*17*256"; + ++test; + if (run_binary_cleanup(binary, "-5", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 174848 + 5 * 17 * 256) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Size of empty D81 image should be 80*40*256"; + ++test; + if (run_binary_cleanup(binary, "", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (size == 80 * 40 * 256) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "Writing file with one block should fill track 1 sector 0"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 0, 37) && image[0 * 256] == 0 && image[0 * 256 + 1] == (char)255) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Diskname should be found in track 18 sector 0 offset $90"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-n 0123456789abcdef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 0x90], "0123456789ABCDEF", 16) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Diskname should be found in track 40 sector 0 offset 4 for d81"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-n 0123456789abcdef -w 1.prg", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[40*39*256 + 4], "0123456789ABCDEF", 16) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Diskname should be truncated to 16 characters"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-n 0123456789abcdef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 0x90], "0123456789ABCDEF", 16) == 0 && image[track_offset[17] + 0xa0] == (char)0xa0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Diskname hex escape should be evaluated correctly"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-n 0123456789abcde#ef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 0x90 + 15] == (char)0xef) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Disk ID should be found in track 18 sector 0 offset $a2"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-i 01234 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 0xa2], "01234", 5) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Disk ID should be found in track 40 sector 0 offset 0x16 for d81"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-i 01234 -w 1.prg", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[40 * 39 * 256 + 0x16], "01234", 5) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Disk ID should be truncated to 5 characters"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-i 0123456789ABCDEFGHI -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 0xa2], "01234", 5) == 0 && image[track_offset[17] + 0xa7] == (char)0xa0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Disk ID hex escape should be evaluated correctly"; + ++test; + create_value_file("1.prg", 254, 37); + if (run_binary_cleanup(binary, "-i 0123#ef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 0xa2 + 4] == (char)0xef) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Setting minimum sector to 7 should fill track 1 sector 7"; + ++test; + create_value_file("1.prg", 254 * 1, 1); + if (run_binary_cleanup(binary, "-F 7 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 7, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Setting minimum sector to 7 for second track should fill track 2 sector 7"; + ++test; + create_value_file("1.prg", 254 * 21, 1); + create_value_file("2.prg", 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -F 7 -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 21 + 7, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Setting minimum sector to 7 should fill track 2 sector 7 for longer file"; + ++test; + create_value_file("1.prg", 254 * 22, 1); + if (run_binary_cleanup(binary, "-F 7 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 21 + 7, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Setting minimum sector to negative for second track should fill track 2 sector 10"; + ++test; + create_value_file("1.prg", 254 * 22, 1); + if (run_binary_cleanup(binary, "-F -3 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 21 + 4, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Minimum sector should fall back to 0 after track change"; + ++test; + create_value_file("1.prg", 254 * 21, 1); + create_value_file("2.prg", 254, 2); + if (run_binary_cleanup(binary, "-F 7 -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 21 + 0, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File with default sector interleave 7 should fill sector 0 and 7 on track 1"; + ++test; + create_value_file("1.prg", 254 * 2, 37); + if (run_binary_cleanup(binary, "-S 7 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 0, 37) && block_is_filled(image, 7, 37)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File with sector interleave 9 should fill sector 0 and 9 on track 1"; + ++test; + create_value_file("1.prg", 254 * 2, 37); + if (run_binary_cleanup(binary, "-s 9 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 0, 37) && block_is_filled(image, 9, 37)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File with sector interleave 20 should fill sector 3 and 1 on track 1"; + ++test; + create_value_file("1.prg", 254 * 2, 37); + if (run_binary_cleanup(binary, "-F 3 -s 20 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 3, 37) && block_is_filled(image, 2, 37)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Sector interleave should go back to default for next file"; + ++test; + create_value_file("1.prg", 254 * 2, 1); + create_value_file("2.prg", 254 * 2, 2); + if (run_binary_cleanup(binary, "-S 3 -s 2 -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 4, 2) && block_is_filled(image, 7, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Filename should be found at track 18 sector 1 offset 5"; + ++test; + create_value_file("1.prg", 254 * 2, 1); + if (run_binary_cleanup(binary, "-f 0123456789abcdef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 256 + 5], "0123456789ABCDEF", 16) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Filename hexvalue should be interpreted correctly"; + ++test; + create_value_file("1.prg", 254 * 2, 1); + if (run_binary_cleanup(binary, "-f 0123456789ABCDE#ef -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 5 + 15] == (char)0xef) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Input path should be stripped of folders for filename"; + ++test; + create_value_file(".." FILESEPARATOR "1.prg", 254 * 2, 1); + if (run_binary_cleanup(binary, "-w .." FILESEPARATOR "1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + 256 + 5], "1.PRG", 5) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("../1.prg"); + + description = "Second file should start on track 2 sector 10 for -e"; + ++test; + create_value_file("1.prg", 254, 1); + create_value_file("2.prg", 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -e -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 0 + 21 + 10, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Second file should start on track 2 sector 3 for -e -b 3"; + ++test; + create_value_file("1.prg", 254, 1); + create_value_file("2.prg", 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -e -b 3 -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 3 + 21, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Second file should start on track 1 when it fits for -E"; + ++test; + create_value_file("1.prg", 20 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -E -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 11, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Second file should not start on track 1 when it does not fit for -E"; + ++test; + create_value_file("1.prg", 20 * 254, 1); + create_value_file("2.prg", 2 * 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -E -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 11, 0)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File should start on track 13 for -r"; + ++test; + create_value_file("1.prg", 254, 1); + if (run_binary_cleanup(binary, "-r 13 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[12] / 256 + 0, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File should start on sector 14 for -b"; + ++test; + create_value_file("1.prg", 254, 1); + create_value_file("2.prg", 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -b 14 -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 14, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File should be distributed to both sides for -c"; + ++test; + create_value_file("1.prg", 22 * 254, 1); + if (run_binary_cleanup(binary, "-c -w 1.prg", "image.d71", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset_b[0] / 256 + 0, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File should cover 1 sector on track 19 for -x not set"; + ++test; + create_value_file("1.prg", 356 * 254, 1); /* leaves only one sector free before track 18 */ + create_value_file("2.prg", 2 * 254, 2); + if (run_binary_cleanup(binary, "-w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[18] / 256 + 13, 0)) { /* check only second sector */ + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File should cover 2 sectors on track 19 for -x"; + ++test; + create_value_file("1.prg", 356 * 254, 1); /* leaves only one sector free before track 18 */ + create_value_file("2.prg", 2 * 254, 2); + if (run_binary_cleanup(binary, "-x -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[18] / 256 + 10, 2)) { /* check only second sector */ + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File should be placed on track 18 for -t"; + ++test; + create_value_file("1.prg", 357 * 254, 1); /* fills all tracks up to 18 */ + create_value_file("2.prg", 2 * 254, 2); + if (run_binary_cleanup(binary, "-t -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[17] / 256 + 12, 2)) { /* check only second sector, 2 sectors are filled by dir */ + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File with 3 sectors should be placed on track 18 for -u"; + ++test; + create_value_file("1.prg", 357 * 254, 1); /* fills all tracks up to 18 */ + create_value_file("2.prg", 3 * 254, 2); + if (run_binary_cleanup(binary, "-t -u 3 -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[17] / 256 + 3 /* (3+10+10)%19-1 */, 2)) { /* check only third sector */ + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Track 23 sector 0 and 1 should be identical to track 18 for -d"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + create_value_file("2.prg", 5 * 254, 2); + if (run_binary_cleanup(binary, "-d 23 -w 1.prg -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (memcmp(&image[track_offset[17] + 1], &image[track_offset[22] + 1], 1) == 0 /* shadow BAM is not valid, only need the sector link */ + && memcmp(&image[track_offset[17] + 1] + 256, &image[track_offset[22] + 1] + 256, 255) == 0) { + /* leave out next dir block track */ + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "File should have DIR block size 0 for -B"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + if (run_binary_cleanup(binary, "-B 0 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 30] == 0 && image[track_offset[17] + 256 + 31] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File should have DIR block size 65535 for -B"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + if (run_binary_cleanup(binary, "-B 65535 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 30] == (char)255 && image[track_offset[17] + 256 + 31] == (char)255) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Loop file should have actual DIR block size per default"; + ++test; + create_value_file("1.prg", 258 * 254, 1); + if (run_binary_cleanup(binary, "-w 1.prg -f LOOP.PRG -l 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 32 + 30] == 2 && image[track_offset[17] + 256 + 32 + 31] == 1) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Loop file should have DIR block size 258 for -B"; + ++test; + create_value_file("1.prg", 39 * 254, 1); + if (run_binary_cleanup(binary, "-w 1.prg -f LOOP.PRG -B 258 -l 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 32 + 30] == 2 && image[track_offset[17] + 256 + 32 + 31] == 1) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File should have DIR block size 258 for -B, but actual block size in shadow dir for -d"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + if (run_binary_cleanup(binary, "-B 258 -d 23 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 30] == 258%256 && image[track_offset[17] + 256 + 31] == 258/256 && image[track_offset[22] + 256 + 30] == 3 && image[track_offset[22] + 256 + 31] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Loop file should share track and sector with later file using -l"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-f LOOP -l 1.prg -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if ((image[track_offset[17] + 256 + 3] == image[track_offset[17] + 256 + 32 + 3]) && (image[track_offset[17] + 256 + 4] == image[track_offset[17] + 256 + 32 + 4])) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Loop file should share track and sector with file using -l when modifying image"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary(binary, "-f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-f LOOP -l FILE", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if ((image[track_offset[17] + 256 + 3] == image[track_offset[17] + 256 + 32 + 3]) && (image[track_offset[17] + 256 + 4] == image[track_offset[17] + 256 + 32 + 4])) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing a PRG should result in first two blocks allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-F 0 -S 1 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfc) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing a PRG should result in first two blocks allocated on d81"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f FILE -w 1.prg", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[40*39*256+256 + 6 + 11] == (char)0xfc) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing a DEL should result in first two blocks allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-T DEL -F 0 -S 1 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfc) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing two files with type 0 should result in first two blocks allocated"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-T 0 -S 1 -F 0 -f 1 -w 1.prg -T 0 -f 2 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfc) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing no file with -L should create dir entry, but not allocate any block"; + ++test; + if (run_binary_cleanup(binary, "-F 0 -S 1 -f FILE -L", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if ((image[track_offset[17] + 256 + 2] == (char)0x82) && image[track_offset[17] + 5] == (char)0xff) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Overwriting a PRG should result in only first block allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary(binary, "-F 0 -S 1 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-F 0 -S 1 -f FILE -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfe) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Overwriting a non-standard file type should result in only first block allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary(binary, "-F 0 -S 1 -T 69 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-F 0 -S 1 -f FILE -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfe) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Overwriting a PRG on d81 should result in only first block allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary(binary, "-f FILE -w 1.prg", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-f FILE -w 2.prg", "image.d81", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[40 * 39 * 256 + 256 + 6 + 11] == (char)0xfe) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Overwriting a PRG with a USR should result in only first block allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary(binary, "-F 0 -S 1 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-T USR -F 0 -S 1 -f FILE -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfe) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Overwriting a PRG with a DEL should result in only first block allocated"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary(binary, "-F 0 -S 1 -f FILE -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-T DEL -F 0 -S 1 -f FILE -w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 5] == (char)0xfe) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Protect flag should be set for -P"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-P -f file1 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 2] == (char)0xc2) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Protect flag should be set for transwarp file for -P"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-P -f file1 -W 1.prg -w \"transwarp v0.84.prg\"", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 2] == (char)0xc2) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Open flag should be set for -O"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-O -f file1 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 2] == (char)0x02) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Open flag should be set for transwarp file for -O"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-O -f file1 -W 1.prg -w \"transwarp v0.84.prg\"", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 2] == (char)0x02) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "After having set type, open and protected flag next file should go back to normal PRG as default"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-T USR -P -O -f file1 -w 1.prg -f file2 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 32 + 2] == (char)0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "After having set type, open and protected flag next file should go back to normal PRG as default"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-T USR -P -O -f file1 -w 1.prg -f file2 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 32 + 2] == (char)0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "File should be overwritten even if there is a free dir slot before"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary(binary, "-f file1 -w 1.prg -f file2 -w 1.prg -f file1 -T DEL -O -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-f file2 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 +2] == (char)0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing 9 files should allocate new dir sector"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg -f 3 -w 1.prg -f 4 -w 1.prg -f 5 -w 1.prg -f 6 -w 1.prg -f 7 -w 1.prg -f 8 -w 1.prg -f 9 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 256 + 3*256 + 2] == (char)0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Directory should allow for 144 entries"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary(binary, "-f 00 -w 1.prg -f 01 -w 1.prg -f 02 -w 1.prg -f 03 -w 1.prg -f 04 -w 1.prg -f 05 -w 1.prg -f 06 -w 1.prg -f 07 -w 1.prg -f 08 -w 1.prg -f 09 -w 1.prg -f 0a -w 1.prg -f 0b -w 1.prg -f 0c -w 1.prg -f 0d -w 1.prg -f 0e -w 1.prg -f 0f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 10 -w 1.prg -f 11 -w 1.prg -f 12 -w 1.prg -f 13 -w 1.prg -f 14 -w 1.prg -f 15 -w 1.prg -f 16 -w 1.prg -f 17 -w 1.prg -f 18 -w 1.prg -f 19 -w 1.prg -f 1a -w 1.prg -f 1b -w 1.prg -f 1c -w 1.prg -f 1d -w 1.prg -f 1e -w 1.prg -f 1f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 20 -w 1.prg -f 21 -w 1.prg -f 22 -w 1.prg -f 23 -w 1.prg -f 24 -w 1.prg -f 25 -w 1.prg -f 26 -w 1.prg -f 27 -w 1.prg -f 28 -w 1.prg -f 29 -w 1.prg -f 2a -w 1.prg -f 2b -w 1.prg -f 2c -w 1.prg -f 2d -w 1.prg -f 2e -w 1.prg -f 2f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 30 -w 1.prg -f 31 -w 1.prg -f 32 -w 1.prg -f 33 -w 1.prg -f 34 -w 1.prg -f 35 -w 1.prg -f 36 -w 1.prg -f 37 -w 1.prg -f 38 -w 1.prg -f 39 -w 1.prg -f 3a -w 1.prg -f 3b -w 1.prg -f 3c -w 1.prg -f 3d -w 1.prg -f 3e -w 1.prg -f 3f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 40 -w 1.prg -f 41 -w 1.prg -f 42 -w 1.prg -f 43 -w 1.prg -f 44 -w 1.prg -f 45 -w 1.prg -f 46 -w 1.prg -f 47 -w 1.prg -f 48 -w 1.prg -f 49 -w 1.prg -f 4a -w 1.prg -f 4b -w 1.prg -f 4c -w 1.prg -f 4d -w 1.prg -f 4e -w 1.prg -f 4f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 50 -w 1.prg -f 51 -w 1.prg -f 52 -w 1.prg -f 53 -w 1.prg -f 54 -w 1.prg -f 55 -w 1.prg -f 56 -w 1.prg -f 57 -w 1.prg -f 58 -w 1.prg -f 59 -w 1.prg -f 5a -w 1.prg -f 5b -w 1.prg -f 5c -w 1.prg -f 5d -w 1.prg -f 5e -w 1.prg -f 5f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 60 -w 1.prg -f 61 -w 1.prg -f 62 -w 1.prg -f 63 -w 1.prg -f 64 -w 1.prg -f 65 -w 1.prg -f 66 -w 1.prg -f 67 -w 1.prg -f 68 -w 1.prg -f 69 -w 1.prg -f 6a -w 1.prg -f 6b -w 1.prg -f 6c -w 1.prg -f 6d -w 1.prg -f 6e -w 1.prg -f 6f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary(binary, "-f 70 -w 1.prg -f 71 -w 1.prg -f 72 -w 1.prg -f 73 -w 1.prg -f 74 -w 1.prg -f 75 -w 1.prg -f 76 -w 1.prg -f 77 -w 1.prg -f 78 -w 1.prg -f 79 -w 1.prg -f 7a -w 1.prg -f 7b -w 1.prg -f 7c -w 1.prg -f 7d -w 1.prg -f 7e -w 1.prg -f 7f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-f 80 -w 1.prg -f 81 -w 1.prg -f 82 -w 1.prg -f 83 -w 1.prg -f 84 -w 1.prg -f 85 -w 1.prg -f 86 -w 1.prg -f 87 -w 1.prg -f 88 -w 1.prg -f 89 -w 1.prg -f 8a -w 1.prg -f 8b -w 1.prg -f 8c -w 1.prg -f 8d -w 1.prg -f 8e -w 1.prg -f 8f -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (strncmp(&image[track_offset[17] + (1+18*3)%19*256 + 7*32 + 5], "8F", 2) == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Writing a duplicate DIR entry with -N should write a new file"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary_cleanup(binary, "-m -f 1 -w 1.prg -N -f 1 -w 2.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, 10, 2)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Writing a duplicate DIR entry with -o should return an error"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary(binary, "-m -f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (run_binary_cleanup(binary, "-m -o -f 1 -w 1.prg ", "image.d64", &image, &size, true) != NO_ERROR) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Sector chain with invalid track link should be re-added to dir but left invalid for -R 0"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 0 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[10 * 256] == 36) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring scratched first file should regenerate the dir entry for -R 1"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratched */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring huge scratched file should regenerate the dir entry for -R 1"; + ++test; + create_value_file("1.prg", 664 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratched */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring scratched last file should regenerate the dir entry for -R 1"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg -f 3 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2*32 + 2] = 0; /* scratched */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2*32 + 2] == 0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring scratched mid file should regenerate the dir entry for -R 1"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg -f 3 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratched */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 1*32 + 2] == 0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring scratched file should result in correct file size for -R 1"; + ++test; + create_value_file("1.prg", (1*256 + 13) * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 30] = 0; /* delete file size */ + image[track_offset[17] + 256 + 31] = 0; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 30] == 13 && (unsigned char)image[track_offset[17] + 256 + 31] == 1) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring 10 files with same name should create correct filename appendix for -R 1"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-m -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg -N -f file -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 2*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 4*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 5*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 6*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 7*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 4*256 + 0*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 4*256 + 1*32 + 2] = 0; /* scratch file */ + image[track_offset[17] + 4*256 + 2*32 + 2] = 0; /* scratch file */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 4*256 + 2*32 + 10] == '1' && (unsigned char)image[track_offset[17] + 4*256 + 2*32 + 11] == '0') { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring unlinked dir sector should link it for -R 1"; + ++test; + create_value_file("1.prg", 1 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg -f 3 -w 1.prg -f 4 -w 1.prg -f 5 -w 1.prg -f 6 -w 1.prg -f 7 -w 1.prg -f 8 -w 1.prg -f 9 -w 1.prg -f 10 -w 1.prg -f 11 -w 1.prg -f 12 -w 1.prg -f 13 -w 1.prg -f 14 -w 1.prg -f 15 -w 1.prg -f 16 -w 1.prg -f 17 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 1*256 + 1] = 7; /* make first sector link point to third dir sector */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 7*256 + 1] == 4) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Scratched file with conflicting t/s chain should be ignored for -R 1"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[20*256+1] = 10; /* make t/s link point to second sector of first file */ + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch second file */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 1*32 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring should be robust against invalid track pointer in directory for -R 1"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 1 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else { + result = TEST_PASS; + ++passed; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild valid sector chain should be restored for -R 2"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = (unsigned char)0xff; /* overwrite track pointer */ + image[track_offset[17] + 256 + 4] = (unsigned char)0xff; /* overwrite sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && (unsigned char)image[track_offset[17] + 256 + 3] == 1 && (unsigned char)image[track_offset[17] + 256 + 4] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Restoring wild sector chain should result in correct file size for -R 2"; + ++test; + create_value_file("1.prg", (1*256 + 13) * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[track_offset[17] + 256 + 30] = 0; /* delete file size */ + image[track_offset[17] + 256 + 31] = 0; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 30] == 13 && (unsigned char)image[track_offset[17] + 256 + 31] == 1) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with invalid track link should be ignored for -R 2"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with invalid sector link should be ignored for -R 2"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[10 * 256 + 0] = 1; + image[10 * 256 + 1] = 21; /* invalid sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with conflict should be ignored for -R 2"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[20*256+1] = 10; /* make t/s link point to second sector of first file */ + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 1*32 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 1*32 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 1*32 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with loop should be ignored for -R 2"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[10*256+0] = 1; /* make t/s link loop to first block */ + image[10*256+1] = 0; + image[track_offset[17] + 256 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild valid single sector should be ignored for -R 2"; + ++test; + create_value_file("1.prg", 1 * 113, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 2 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Scratched file with invalid track link should be fixed for -R 3"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 3 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[10 * 256] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Fixed file with long name should receive marker for -R 3"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 0123456789123456 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 3 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 20] == '<') { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Fixed file with existing name should receive appendix and marker for -R 3"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-m -f file1 -w 1.prg -N -f \"file1<\" -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 3 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 13] == '<') { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Conflicting t/s chain should result in correct file size for -R 3"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + create_value_file("2.prg", 2 * 254, 2); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 2.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[20*256+1] = 10; /* make t/s link point to second sector of first file */ + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 1*32 + 30] = (unsigned char)0xff; /* overwrite file size */ + image[track_offset[17] + 256 + 1*32 + 31] = (unsigned char)0xff; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 3 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 +1*32 + 2] == 0x82 && (unsigned char)image[track_offset[17] + 256 + 1*32 + 30] == 1 && (unsigned char)image[track_offset[17] + 256 + 1*32 + 31] == 0) { + result = TEST_PASS; + ++passed; + } else { + printf(" (%d/%d) ", (unsigned char)image[track_offset[17] + 256 + 1*32 + 30], (unsigned char)image[track_offset[17] + 256 + 1*32 + 31]); + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with invalid track link should be fixed for -R 4"; + ++test; + create_value_file("1.prg", 254 + 11, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[10 * 256] = 36; /* invalid track pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[10 * 256] == 0 && (unsigned char)image[10 * 256 + 1] == 0xff) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with invalid sector link should be fixed for -R 4"; + ++test; + create_value_file("1.prg", 254 + 11, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[10 * 256 + 0] = 1; + image[10 * 256 + 1] = 21; /* invalid sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[10 * 256] == 0 && (unsigned char)image[10 * 256 + 1] == 0xff) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with conflict should be fixed for -R 4"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[19*256+1] = 10; /* make t/s link point to second sector of first file */ + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 1*32 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 1*32 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 1*32 + 2] == 0x82 && image[19*256] == 0 && (unsigned char)image[19*256+1] == 0xff) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild sector chain with loop should be fixed for -R 4"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[10*256+0] = 1; /* make t/s link loop to first block */ + image[10*256+1] = 0; + image[track_offset[17] + 256 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[10 * 256] == 0 && (unsigned char)image[10 * 256 + 1] == 0xff) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild valid single sector should be ignored for -R 4"; + ++test; + create_value_file("1.prg", 1 * 113, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Scratched file with immediate conflict should be ignored for -R 4"; + ++test; + create_value_file("1.prg", 3 * 254, 1); + create_value_file("2.prg", 1 * 254, 2); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg -f 2 -w 2.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 1*32 + 2] = 0; /* scratch second file */ + image[track_offset[17] + 256 + 1*32 + 3] = 1; /* let t/s point to first file */ + image[track_offset[17] + 256 + 1*32 + 4] = 0; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 4 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 1*32 + 2] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "Wild valid single sector should be restored for -R 5"; + ++test; + create_value_file("1.prg", 1 * 113, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 5 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Wild valid single sector should have correct size for -R 5"; + ++test; + create_value_file("1.prg", 1 * 113, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratch file */ + image[track_offset[17] + 256 + 3] = 0; /* delete track pointer */ + image[track_offset[17] + 256 + 4] = 0; /* delete sector pointer */ + image[track_offset[17] + 256 + 30] = (unsigned char)0xff; /* overwrite file size */ + image[track_offset[17] + 256 + 31] = (unsigned char)0xff; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-R 5 ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if((unsigned char)image[track_offset[17] + 256 + 2] == 0x82 && image[track_offset[17] + 256 + 30] == 1 && image[track_offset[17] + 256 + 31] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Recursive DIR sector chain should not run into endless loop"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 0] = 18; /* track link points to dir track */ + image[track_offset[17] + 256 + 1] = 1; /* sector link points to itself */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-f 2 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else { + result = TEST_PASS; + ++passed; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Recursive file sector chain should not run into endless loop for overwriting"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[10 * 256 + 0] = 1; /* t/s link points to first block */ + image[10 * 256 + 1] = 0; + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-f 1 -w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else { + result = TEST_PASS; + ++passed; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Should not return error for illegal t/s link in DIR chain"; + ++test; + create_value_file("1.prg", 254 + 11, 1); + if (run_binary_cleanup(binary, "-w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 0] = 18; /* legal track */ + image[track_offset[17] + 256 + 1] = 127; /* illegal sector */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "", "image.d64", &image, &size, true) == NO_ERROR) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Should not return error for illegal t/s pointer in DIR"; + ++test; + create_value_file("1.prg", 254 + 11, 1); + if (run_binary_cleanup(binary, "-w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 4] = 127; /* illegal sector */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "", "image.d64", &image, &size, true) == NO_ERROR) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Verbose output should not return error for illegal t/s pointer in DIR"; + ++test; + create_value_file("1.prg", 254 + 11, 1); + if (run_binary_cleanup(binary, "-w 1.prg ", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 4] = 127; /* illegal sector */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-v", "image.d64", &image, &size, true) == NO_ERROR) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Adding a transwarp v0.84 file should allocate track 1 completely"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-r 1 -f file1 -W 1.prg -w \"transwarp v0.84.prg\"", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 4] == 0 && image[track_offset[17] + 5] == 0 && image[track_offset[17] + 6] == 0 && image[track_offset[17] + 7] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Adding a transwarp v0.86 file should allocate track 1 completely"; + ++test; + create_value_file("1.prg", 2 * 254, 1); + if (run_binary_cleanup(binary, "-r 1 -f file1 -W 1.prg -w \"transwarp v0.86.prg\"", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17] + 4] == 0 && image[track_offset[17] + 5] == 0 && image[track_offset[17] + 6] == 0 && image[track_offset[17] + 7] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Sector on new track should not be limited to number of sectors on old track"; + ++test; + create_value_file("1.prg", 254 * 20, 1); /* track 24 has 19 blocks, track 25 only 18 */ + if (run_binary_cleanup(binary, "-r 24 -F 18 -w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (block_is_filled(image, track_offset[24]/256, 1)) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + + description = "Last file block should set unused bytes to zero"; + ++test; + create_value_file("1.prg", 254 * 1, 1); + create_value_file("2.prg", 1, 2); + if (run_binary_cleanup(binary, "-w 1.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + image[track_offset[17] + 256 + 2] = 0; /* scratched */ + image[track_offset[17] + 5] = (char)0xff; /* deallocated in BAM */ + write_file("image.d64", size, image); + if (run_binary_cleanup(binary, "-w 2.prg", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } + if(result != TEST_UNRESOLVED) { + result = TEST_PASS; + if(image[2] != 2) { + result = TEST_FAIL; + } + for(int b = 3; b < 256; b++) { + if(image[b] != 0) { + result = TEST_FAIL; + } + } + if(result == TEST_PASS) { + ++passed; + } + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + remove("1.prg"); + remove("2.prg"); + + description = "BAM message for D64 should be limited to 85 chars"; + ++test; + if (run_binary_cleanup(binary, "-H 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17]+0xab] == '0' && image[track_offset[17]+255] == '4' && image[track_offset[17]+256] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + description = "BAM message for SPEED DOS should be limited to 20 chars"; + ++test; + if (run_binary_cleanup(binary, "-4 -H 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", "image.d64", &image, &size, false) != NO_ERROR) { + result = TEST_UNRESOLVED; + } else if (image[track_offset[17]+0xab] == '0' && image[track_offset[17]+0xab+19] == '9' && image[track_offset[17]+0xab+20] == 0) { + result = TEST_PASS; + ++passed; + } else { + result = TEST_FAIL; + } + printf("%0*d: %s: %s\n", test_pad, test, result_str[result], description); + + /* ideas for tests: + - test writing of transwarp files + - test encryption of transwarp files + - test allocation for transwarp files (like CBM DOS: from 17 downwards, then from 19 upwards) + - test -r for transwarp + - test reasonable behaviour of -e, -E, -s and -b for transwarp (should they all be rejected?) + - test -V + - test filename hash collision with different -M values + - check -g in more detail? + - check output for -q? + */ + + /* clean up */ + if (image != NULL) { + free(image); + } + + /* print summary */ + printf("\nPassed %d of %d tests.\n", passed, test); + if (passed == test) { + return 0; + } + return 1; +} diff --git a/loader/tools/cc1541/test_cc1541.vcxproj b/loader/tools/cc1541/test_cc1541.vcxproj new file mode 100644 index 0000000..3ad5fe4 --- /dev/null +++ b/loader/tools/cc1541/test_cc1541.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4206D44B-8628-4E98-9F6F-775C8AC5E1B9} + Win32Proj + test_cc1541 + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(ProjectName)\$(Configuration)\ + + + false + + + false + $(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/loader/tools/cc1541/test_cc1541.vcxproj.filters b/loader/tools/cc1541/test_cc1541.vcxproj.filters new file mode 100644 index 0000000..a1c9876 --- /dev/null +++ b/loader/tools/cc1541/test_cc1541.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/loader/tools/cc1541/transwarp v0.84.prg b/loader/tools/cc1541/transwarp v0.84.prg new file mode 100644 index 0000000..37af81d Binary files /dev/null and b/loader/tools/cc1541/transwarp v0.84.prg differ diff --git a/loader/tools/cc1541/transwarp v0.86.prg b/loader/tools/cc1541/transwarp v0.86.prg new file mode 100644 index 0000000..e89a2c8 Binary files /dev/null and b/loader/tools/cc1541/transwarp v0.86.prg differ diff --git a/loader/tools/compressedfileconverter.pl b/loader/tools/compressedfileconverter.pl new file mode 100755 index 0000000..df7b0a2 --- /dev/null +++ b/loader/tools/compressedfileconverter.pl @@ -0,0 +1,85 @@ +#!/usr/bin/env perl + +=head1 NAME + +compressedfileconverter.pl + +=head1 DESCRIPTION + +This script converts files compressed by some crunchers +so they can be loaded and decompressed in-place, on the fly. + +=head1 SYNOPSIS + + compressedfileconverter.pl [dnx|lc] uncompressed_infile compressed_infile outfile + +=cut + +use strict; +use warnings; + + +my %margins = ( + '' => 0, + 'dnx' => 4, + 'lc' => 3 +); + +my $type = ''; +if (scalar @ARGV == 4) { + $type = shift @ARGV; +} + +if (!($type ~~ %margins) or (scalar @ARGV != 3)) { + die "Usage: $0 [dnx|lc] uncompressed_infile compressed_infile outfile\n"; +} + +my $uncompressed_infile = shift @ARGV; +my $compressed_infile = shift @ARGV; +my $converted_outfile = shift @ARGV; + + +open UNCOMPRESSED, $uncompressed_infile + or die "\nCan't open uncompressed file $uncompressed_infile for reading: $!\n"; +binmode UNCOMPRESSED; + +my $uncompressed_address; +read(UNCOMPRESSED, $uncompressed_address, 2); +my $uncompressed_data; +my $uncompressed_size = read(UNCOMPRESSED, $uncompressed_data, 65536); +$uncompressed_address = unpack("S", $uncompressed_address); +close UNCOMPRESSED; + + +open COMPRESSED, $compressed_infile + or die "\nCan't open compressed file $compressed_infile for reading: $!\n"; +binmode COMPRESSED; + +my $compressed_data; +if (!($type eq 'lc')) { + my $compressed_address; + read(COMPRESSED, $compressed_address, 2); +} +my $compressed_size = read(COMPRESSED, $compressed_data, 65536); +close COMPRESSED; + +open CONVERTED, ">$converted_outfile" + or die "\nCan't open converted file $converted_outfile for writing: $!\n"; +binmode CONVERTED; + +my $offset = 0; +my $safety_margin = $margins{$type}; +$compressed_size += $offset; +my $converted_address = $uncompressed_address + $uncompressed_size + $safety_margin - $compressed_size; + + +$converted_address = pack("S", $converted_address); +print CONVERTED $converted_address; +if ($offset) { + $uncompressed_address = pack("S<", $uncompressed_address); + print CONVERTED $uncompressed_address; +} +print CONVERTED $compressed_data; +close CONVERTED; + +exit 0; diff --git a/loader/tools/dali/Makefile b/loader/tools/dali/Makefile new file mode 100644 index 0000000..76283d3 --- /dev/null +++ b/loader/tools/dali/Makefile @@ -0,0 +1,55 @@ +ACME ?= acme +ACME_OPTS ?= -f cbm +DALI_CFLAGS ?= $(CFLAGS) -Os -Wall +CC ?= gcc +SALVADOR_PATH = salvador +SALVADOR_CFLAGS = -O3 -g -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc -fPIC +SALVADOR_CC = gcc +SALVADOR_OBJS = $(SALVADOR_PATH)/obj/src/*.o $(SALVADOR_PATH)/obj/src/libdivsufsort/lib/*.o + +V ?= 0 +ifeq ($(V),1) +VR:= +else +VR:=@ +endif + +all: dali + +dali: dali.c sfx_small.h sfx_fast.h salvador.a + @echo "Building dali..." + $(VR)$(CC) $(DALI_CFLAGS) -o $@ $^ + $(VR)strip $@ + +salvador.a: + @echo "Creating salvador library..." + $(VR)make CFLAGS='$(SALVADOR_CFLAGS)' CC='$(SALVADOR_CC)' -C $(SALVADOR_PATH) + $(VR)ar rcs $@ $(SALVADOR_OBJS) + $(VR)objcopy --redefine-sym main=salvador_main $@ + +depack.prg: depack.asm dzx0.asm ../../benchmark/files/a.lz + $(ACME) $(ACME_OPTS) -o $@ $< + +sfx_small.h: sfx.asm + @echo "Compiling sfx code for dali..." + $(VR)$(ACME) $(ACME_OPTS) -l $(basename $@).lst -o $(basename $@) $< + $(VR)grep 'DALI' $(basename $@).lst | sed -e 's/[[:space:]]*;[[:space:]]*.*//g' -e 's/[[:space:]]*//g' -e 's/\=\$$/ 0x/g' -e 's/^/#define /' > $@ + $(VR)echo 'static const char decruncher_small[] = {' >> $@ + @#create a hexdump, add a marker (+) where lines are truncated (each 50 chars = 8 bytes per line), substitute marker (+) with newline (use tr here, as bsd-sed fails on \n), add identation to each line + $(VR)hexdump -ve '1/1 "0x%.2x,"' $(basename $@) | sed -e 's/,$$/+/g' -e 's/.\{50\}/&+/g' | tr -s '+' '\n' | sed 's/^/& /g' >> $@ + $(VR)echo '};' >> $@ + $(VR)rm $(basename $@).lst $(basename $@) + +sfx_fast.h: sfx.asm + @echo "Compiling sfx code for dali..." + $(VR)$(ACME) $(ACME_OPTS) -DSFX_FAST=1 -l $(basename $@).lst -o $(basename $@) $< + $(VR)grep 'DALI' $(basename $@).lst | sed -e 's/[[:space:]]*;[[:space:]]*.*//g' -e 's/[[:space:]]*//g' -e 's/\=\$$/ 0x/g' -e 's/^/#define /' > $@ + $(VR)echo 'static const char decruncher[] = {' >> $@ + @#create a hexdump, add a marker (+) where lines are truncated (each 50 chars = 8 bytes per line), substitute marker (+) with newline (use tr here, as bsd-sed fails on \n), add identation to each line + $(VR)hexdump -ve '1/1 "0x%.2x,"' $(basename $@) | sed -e 's/,$$/+/g' -e 's/.\{50\}/&+/g' | tr -s '+' '\n' | sed 's/^/& /g' >> $@ + $(VR)echo '};' >> $@ + $(VR)rm $(basename $@).lst $(basename $@) + +clean: + $(VR)-rm dali sfx_small.h sfx_fast.h depack.prg salvador.a + $(VR)make -C salvador clean diff --git a/loader/tools/dali/dali.c b/loader/tools/dali/dali.c new file mode 100644 index 0000000..f697ec9 --- /dev/null +++ b/loader/tools/dali/dali.c @@ -0,0 +1,731 @@ +#include +#include +#include +#include +#include "sfx_small.h" +#include "sfx_fast.h" + +#define BUFFER_SIZE 65536 /* must be > MAX_OFFSET */ +#define INITIAL_OFFSET 1 + +#define FALSE 0 +#define TRUE 1 +#define DALI_BITS_LEFT 0 +#define DALI_ELIAS_LE 1 + +typedef struct ctx { + unsigned char *packed_data; + unsigned char *reencoded_data; + unsigned char *unpacked_data; + size_t packed_index; + size_t packed_size; + int packed_bit_mask; + int packed_bit_value; + size_t reencoded_index; + int reencoded_bit_mask; + int reencoded_bit_value; + int reencoded_bit_index; + size_t unpacked_index; + size_t unpacked_size; + int inplace; + + char *output_name; + char *input_name; + char *prefix_name; + char *clamped_name; + + int cbm; + int cbm_orig_addr; + int cbm_packed_addr; + int cbm_range_from; + int cbm_range_to; + int cbm_relocate_packed_addr; + int cbm_relocate_origin_addr; + int cbm_prefix_from; + + int sfx; + int sfx_addr; + int sfx_01; + int sfx_cli; + int sfx_small; + int sfx_size; + char *sfx_code; + int lz_bits; +} ctx; + +void salvador_main(); + +static int read_number(char* arg, char* argname, int limit) { + int number; + if (arg != NULL && arg[0] == '$') number = strtoul(arg + 1, NULL, 16); + else if (arg != NULL && arg[0] == '0' && arg[1] == 'x') number = strtoul(arg + 2, NULL, 16); + else if (arg != NULL && arg[0] >= '0' && arg[0] <= '9') number = strtoul(arg, NULL, 10); + else { + fprintf(stderr, "Error: no valid number given for argument %s (given value is: '%s')\n", argname, arg); + exit(1); + } + if (number < 0 || number > limit) { + fprintf(stderr, "Error: Number '%s' out of range (0 - 65536)\n", arg); + exit(1); + } + return number; +} + +static void file_write_byte(int byte, FILE *ofp) { + if (fputc(byte, ofp) != byte) { + fprintf(stderr, "Error: Cannot write output file\n"); + perror("fputc"); + exit(1); + } + return; +} + +static inline unsigned bit_size(unsigned value) { +# ifdef __GNUC__ +// enum { WORD_BITS = sizeof(unsigned) * CHAR_BIT }; + + return ((sizeof(unsigned) * 8 - 1) ^ __builtin_clz(value)); +# else + signed bits = -1; + + do + ++bits; + while(value >>= 1); + + return bits; +# endif +} + +void write_reencoded_byte(ctx* ctx, int value) { + ctx->reencoded_data[ctx->reencoded_index++] = value; +} + +void write_reencoded_bit(ctx* ctx, int value) { + if (!(ctx->reencoded_bit_mask & 255)) { + if (DALI_BITS_LEFT == 1) { + ctx->reencoded_bit_mask = 0x80; + } else { + ctx->reencoded_bit_mask = 0x1; + } + /* remember position of bit-buffer */ + ctx->reencoded_bit_index = ctx->reencoded_index; + ctx->lz_bits++; + write_reencoded_byte(ctx, 0); + } + if (value) + ctx->reencoded_data[ctx->reencoded_bit_index] |= ctx->reencoded_bit_mask; + if (DALI_BITS_LEFT == 1) { + ctx->reencoded_bit_mask >>= 1; + } else { + ctx->reencoded_bit_mask <<= 1; + } +} + +void write_reencoded_interlaced_elias_gamma(ctx* ctx, int value, int skip) { + int bits = bit_size(value); + int i; + + for (i = 2; i <= value; i <<= 1); + i >>= 1; + + if (DALI_ELIAS_LE) { + if (bits >= 8) { + /* change bit-order, send LSB first */ + /* remove preceeding 1 first */ + value = value & ((0xffff ^ i)); + /* move LSB bits to the beginning */ + value = (value >> 8) | ((value & 0xff) << (bits - 8)); + } + } + + while ((i >>= 1) > 0) { + if (!skip) write_reencoded_bit(ctx, 0); + skip = 0; + write_reencoded_bit(ctx, ((value & i) > 0)); + } + if (!skip) write_reencoded_bit(ctx, 1); +} + +int read_byte(ctx* ctx) { + return ctx->packed_data[ctx->packed_index++]; +} + +int read_bit(ctx* ctx) { + if ((ctx->packed_bit_mask >>= 1) == 0) { + ctx->packed_bit_mask = 0x80; + ctx->packed_bit_value = read_byte(ctx); + } + return (ctx->packed_bit_value & ctx->packed_bit_mask) != 0; +} + +int read_interlaced_elias_gamma(ctx* ctx, int inverted, int skip) { + int value = 1; + /* skip first read bit if skip != 0 */ + while (skip || !read_bit(ctx)) { + skip = 0; + value = (value << 1) | (read_bit(ctx) ^ inverted); + } + return value; +} + +void save_reencoded_stream(ctx* ctx) { +} + +void copy_inplace_literal(ctx* ctx) { + int i; + for (i = ctx->unpacked_index; i < ctx->unpacked_size; i++) { + ctx->reencoded_data[ctx->packed_index] = ctx->unpacked_data[i]; + ctx->packed_index++; + } +} + +void encode_literal(ctx* ctx, int length, int first) { + int i; + if (!first) write_reencoded_bit(ctx, 0); + write_reencoded_interlaced_elias_gamma(ctx, length, 0); + for (i = 0; i < length; i++) { + write_reencoded_byte(ctx, ctx->unpacked_data[ctx->unpacked_index + i]); + } +} + +void encode_rep(ctx* ctx, int length) { + write_reencoded_bit(ctx, 0); + write_reencoded_interlaced_elias_gamma(ctx, length, 0); +} + +void encode_match(ctx* ctx, int length, int offset) { + write_reencoded_bit(ctx, 1); + write_reencoded_interlaced_elias_gamma(ctx, ((offset - 1) >> 7) + 1, 0); + write_reencoded_byte(ctx, (((offset - 1) & 0x7f) << 1) | (length == 2)); + write_reencoded_interlaced_elias_gamma(ctx, length - 1, 1); +} + +void reencode_packed_stream(ctx* ctx) { + int last_offset = INITIAL_OFFSET; + int length; + int overwrite; + + int bit, byte; + int i; + + int safe_input_index = 0; + int safe_output_index = 0; + + int first = 1; + + ctx->packed_index = 0; + ctx->packed_bit_mask = 0; + + ctx->reencoded_index = 0; + ctx->reencoded_bit_mask = 0; + ctx->reencoded_bit_value = 0; + ctx->reencoded_bit_index = 0; + + ctx->packed_index = 0; + ctx->unpacked_index = 0; + + ctx->packed_bit_mask = 0; + ctx->lz_bits = 0; + + while (1) { + /* literal */ + length = read_interlaced_elias_gamma(ctx, FALSE, 0); + for (i = 0; i < length; i++) read_byte(ctx); + encode_literal(ctx, length, first); + first = 0; + + ctx->unpacked_index += length; + + overwrite = (ctx->unpacked_index) - (ctx->unpacked_size - ctx->packed_size + ctx->packed_index); + /* literal would overwrite packed src */ + if (ctx->inplace && overwrite >= 0) { + /* go back to previous index */ + ctx->unpacked_index = safe_input_index; + ctx->packed_index = safe_output_index; + copy_inplace_literal(ctx); + return; + } + /* do remember last safe position */ + safe_input_index = ctx->unpacked_index; + safe_output_index = ctx->packed_index; + + /* copy from new or last offset? */ + bit = read_bit(ctx); + if (!bit) { + /* copy from last_offset */ + length = read_interlaced_elias_gamma(ctx, FALSE, 0); + encode_rep(ctx, length); + + ctx->unpacked_index += length; + + overwrite = (ctx->unpacked_index) - (ctx->unpacked_size - ctx->packed_size + ctx->packed_index); + /* rep would overwrite packed src */ + if (ctx->inplace && overwrite >= 0) { + copy_inplace_literal(ctx); + return; + } + safe_input_index = ctx->unpacked_index; + safe_output_index = ctx->packed_index; + + bit = read_bit(ctx); + } + + while (bit) { + /* copy from new_offset */ + last_offset = read_interlaced_elias_gamma(ctx, TRUE, 0); + if (last_offset == 256) { + if (!ctx->inplace) { + write_reencoded_bit(ctx, 1); + write_reencoded_interlaced_elias_gamma(ctx, last_offset, 0); + } + return; + } + byte = read_byte(ctx); + if (byte & 1) length = 2; + else length = read_interlaced_elias_gamma(ctx, FALSE, 1) + 1; + last_offset = (last_offset << 7) - (byte >> 1); + encode_match(ctx, length, last_offset); + + ctx->unpacked_index += length; + + overwrite = (ctx->unpacked_index) - (ctx->unpacked_size - ctx->packed_size + ctx->packed_index); + /* rep would overwrite packed src */ + if (ctx->inplace && overwrite >= 0) { + copy_inplace_literal(ctx); + return; + } + safe_input_index = ctx->unpacked_index; + safe_output_index = ctx->packed_index; + + bit = read_bit(ctx); + } + } +} + +void write_reencoded_stream(ctx* ctx) { + FILE *fp = NULL; + /* write reencoded output file */ + fp = fopen(ctx->output_name, "wb"); + if (!fp) { + fprintf(stderr, "Error: Cannot create reencoded file (%s)\n", ctx->output_name); + exit(1); + } + + /* as sfx */ + if (ctx->sfx) { + printf("Creating sfx with start-address $%04x\n", ctx->sfx_addr); + if (ctx->sfx_small) { + ctx->sfx_size = sizeof(decruncher_small); + /* copy over to change values in code */ + ctx->sfx_code = (char *)malloc(ctx->sfx_size); + memcpy (ctx->sfx_code, decruncher_small, ctx->sfx_size); + + /* setup jmp target after decompression */ + ctx->sfx_code[DALI_SMALL_SFX_ADDR + 0] = ctx->sfx_addr & 0xff; + ctx->sfx_code[DALI_SMALL_SFX_ADDR + 1] = (ctx->sfx_addr >> 8) & 0xff; + + /* setup decompression destination */ + ctx->sfx_code[DALI_SMALL_DST + 0] = ctx->cbm_orig_addr & 0xff; + ctx->sfx_code[DALI_SMALL_DST + 1] = (ctx->cbm_orig_addr >> 8) & 0xff; + + /* setup compressed data src */ + ctx->sfx_code[DALI_SMALL_SRC + 0] = (0x10000 - ctx->reencoded_index) & 0xff; + ctx->sfx_code[DALI_SMALL_SRC + 1] = ((0x10000 - ctx->reencoded_index) >> 8) & 0xff; + + /* setup compressed data end */ + ctx->sfx_code[DALI_SMALL_DATA_END + 0] = (0x0801 + ctx->sfx_size - 2 + ctx->reencoded_index - 0x100) & 0xff; + ctx->sfx_code[DALI_SMALL_DATA_END + 1] = ((0x0801 + ctx->sfx_size - 2 + ctx->reencoded_index - 0x100) >> 8) & 0xff; + + ctx->sfx_code[DALI_SMALL_DATA_SIZE_HI] = 0xff - (((ctx->reencoded_index + 0x100) >> 8) & 0xff); + } else { + ctx->sfx_size = sizeof(decruncher); + /* copy over to change values in code */ + ctx->sfx_code = (char *)malloc(ctx->sfx_size); + memcpy (ctx->sfx_code, decruncher, ctx->sfx_size); + + if (ctx->sfx_01 < 0) ctx->sfx_01 = 0x37; + + /* setup jmp target after decompression */ + ctx->sfx_code[DALI_FAST_SFX_ADDR + 0] = ctx->sfx_addr & 0xff; + ctx->sfx_code[DALI_FAST_SFX_ADDR + 1] = (ctx->sfx_addr >> 8) & 0xff; + + /* setup decompression destination */ + ctx->sfx_code[DALI_FAST_DST + 0] = ctx->cbm_orig_addr & 0xff; + ctx->sfx_code[DALI_FAST_DST + 1] = (ctx->cbm_orig_addr >> 8) & 0xff; + + /* setup compressed data src */ + ctx->sfx_code[DALI_FAST_SRC + 0] = (0x10000 - ctx->reencoded_index) & 0xff; + ctx->sfx_code[DALI_FAST_SRC + 1] = ((0x10000 - ctx->reencoded_index) >> 8) & 0xff; + + /* setup compressed data end */ + ctx->sfx_code[DALI_FAST_DATA_END + 0] = (0x0801 + ctx->sfx_size - 2 + ctx->reencoded_index - 0x100) & 0xff; + ctx->sfx_code[DALI_FAST_DATA_END + 1] = ((0x0801 + ctx->sfx_size - 2 + ctx->reencoded_index - 0x100) >> 8) & 0xff; + + ctx->sfx_code[DALI_FAST_DATA_SIZE_HI] = 0xff - (((ctx->reencoded_index + 0x100) >> 8) & 0xff); + + ctx->sfx_code[DALI_FAST_01] = ctx->sfx_01; + if (ctx->sfx_cli) ctx->sfx_code[DALI_FAST_CLI] = 0x58; + } + printf("original: $%04x-$%04lx ($%04lx) 100%%\n", ctx->cbm_orig_addr, ctx->cbm_orig_addr + ctx->unpacked_size, ctx->unpacked_size); + printf("packed: $%04x-$%04lx ($%04lx) %3.2f%%\n", 0x0801, 0x0801 + (int)ctx->sfx_size + ctx->packed_index, (int)ctx->sfx_size + ctx->packed_index, ((float)(ctx->packed_index + (int)ctx->sfx_size) / (float)(ctx->unpacked_size) * 100.0)); + + if (fwrite(ctx->sfx_code, sizeof(char), ctx->sfx_size, fp) != ctx->sfx_size) { + fprintf(stderr, "Error: Cannot write output file %s\n", ctx->output_name); + exit(1); + } + /* or standard compressed */ + } else { + if (ctx->cbm_relocate_origin_addr >= 0) { + ctx->cbm_orig_addr = ctx->cbm_relocate_origin_addr; + ctx->cbm = TRUE; + } + + if (ctx->inplace) { + ctx->cbm_packed_addr = ctx->cbm_range_to - ctx->packed_index - 2; + } else { + if (ctx->cbm_relocate_packed_addr >= 0) { + ctx->cbm_packed_addr = ctx->cbm_relocate_packed_addr; + } else { + ctx->cbm_packed_addr = ctx->cbm_orig_addr; + } + } + + + if (ctx->cbm) { + printf("original: $%04x-$%04lx ($%04lx) 100%%\n", ctx->cbm_orig_addr, ctx->cbm_orig_addr + ctx->unpacked_size, ctx->unpacked_size); + printf("packed: $%04x-$%04lx ($%04lx) %3.2f%%\n", ctx->cbm_packed_addr, ctx->cbm_packed_addr + ctx->packed_index + 2, ctx->packed_index + 2, ((float)(ctx->packed_index) / (float)(ctx->unpacked_size) * 100.0)); + if ((ctx->cbm_packed_addr >= 0xd000 && ctx->cbm_packed_addr < 0xe000) || (ctx->cbm_packed_addr < 0xd000 && ctx->cbm_packed_addr + ctx->packed_index + 2 > 0xd000)) { + fprintf(stderr, "Warning: Packed file lies in I/O-range from $d000-$dfff\n"); + } + + /* little endian */ + file_write_byte(ctx->cbm_packed_addr & 255, fp); + file_write_byte((ctx->cbm_packed_addr >> 8) & 255, fp); + + /* big endian, as read backwards by depacker */ + file_write_byte((ctx->cbm_orig_addr >> 8) & 255, fp); + file_write_byte(ctx->cbm_orig_addr & 255, fp); + } else { + printf("original: $%04x-$%04lx ($%04lx) 100%%\n", 0, ctx->unpacked_size, ctx->unpacked_size); + printf("packed: $%04x-$%04lx ($%04lx) %3.2f%%\n", 0, ctx->packed_index, ctx->packed_index, ((float)(ctx->packed_index) / (float)(ctx->unpacked_size) * 100.0)); + } + } + + if (fwrite(ctx->reencoded_data, sizeof(char), ctx->packed_index, fp) != ctx->packed_index) { + fprintf(stderr, "Error: Cannot write output file\n"); + exit(1); + } + fclose(fp); +} + +void do_reencode(ctx* ctx) { + char tmp_name[] = "dict-XXXXXX"; + char src_name[] = "src-XXXXXX"; + unsigned char *dict_data = NULL; + int dict_size = 0; + char *salvador_argv[5]; + int salvador_argc = 0; + int dict_temp = FALSE; + FILE *dfp = NULL; + FILE *sfp = NULL; + FILE* ufp = NULL; + FILE* pfp = NULL; + + + /* determine output filename */ + if (ctx->output_name == NULL) { + ctx->output_name = (char *)malloc(strlen(ctx->input_name) + 4); + strcpy(ctx->output_name, ctx->input_name); + strcat(ctx->output_name, ".lz"); + printf("output name: %s\n", ctx->output_name); + } + + /* allocate buffers */ + ctx->packed_data = (unsigned char *)malloc(BUFFER_SIZE); + ctx->unpacked_data = (unsigned char *)malloc(BUFFER_SIZE + 2); + ctx->reencoded_data = (unsigned char *)malloc(BUFFER_SIZE); + + if (!ctx->packed_data || !ctx->unpacked_data || !ctx->reencoded_data) { + fprintf(stderr, "Error: Insufficient memory\n"); + exit(1); + } + + /* load unpacked file */ + ufp = fopen(ctx->input_name, "rb"); + if (!ufp) { + fprintf(stderr, "Error: Cannot access input file\n"); + exit(1); + } + ctx->unpacked_size = fread(ctx->unpacked_data, sizeof(char), BUFFER_SIZE + 2, ufp); + fclose(ufp); + + /* cbm address handling */ + if (ctx->cbm_relocate_origin_addr >= 0) { + ctx->cbm_orig_addr = ctx->cbm_relocate_origin_addr; + } else { + ctx->cbm_orig_addr = ctx->unpacked_data[0] + (ctx->unpacked_data[1] << 8); + } + + if (ctx->cbm) { + ctx->unpacked_data += 2; + ctx->unpacked_size -= 2; + } + + /* take care of range (--from --to) */ + if (ctx->cbm_range_from < 0) ctx->cbm_range_from = ctx->cbm_orig_addr; + if (ctx->cbm_range_to < 0) ctx->cbm_range_to = ctx->cbm_orig_addr + ctx->unpacked_size; + + if ((ctx->cbm_range_to - ctx->cbm_orig_addr) > ctx->unpacked_size) { + ctx->cbm_range_to = ctx->unpacked_size + ctx->cbm_orig_addr; + fprintf(stderr, "Warning: File ends at $%04x, adopting --to value\n", ctx->cbm_range_to); + } + ctx->unpacked_size = (ctx->cbm_range_to - ctx->cbm_orig_addr); + + /* if range is below start_address, adopt range */ + if (ctx->cbm_range_from < ctx->cbm_orig_addr) { + ctx->cbm_range_from = ctx->cbm_orig_addr; + fprintf(stderr, "Warning: File starts at $%04x, adopting --from value\n", ctx->cbm_range_from); + } + if (ctx->cbm_range_from > ctx->cbm_range_to) { + fprintf(stderr, "Error: --from beyond fileend ($%04x - $%04x)\n", ctx->cbm_range_from, ctx->cbm_range_to); + exit(1); + } + + /* setup dict lengths and position */ + if (ctx->cbm_prefix_from >= 0) { + if (ctx->cbm_range_from < 0) { + fprintf(stderr, "Error: Dict is zero size (use --from)\n"); + exit(1); + } + else if (ctx->cbm_prefix_from >= ctx->cbm_range_from) { + fprintf(stderr, "Error: --from must be greater than --prefix-from\n"); + exit(1); + } + if (ctx->cbm_range_from >= 0 && ctx->cbm_range_from - ctx->cbm_prefix_from > 32640) { + //ctx->cbm_prefix_from = ctx->cbm_range_from - 32640; + fprintf(stderr, "Info: --prefix-from exceeds max offset, not all bytes can be used\n"); + } + /* if range is below start_address, adopt range */ + if (ctx->cbm_prefix_from < ctx->cbm_orig_addr) { + ctx->cbm_prefix_from = ctx->cbm_orig_addr; + fprintf(stderr, "Warning: File starts at $%04x, adopting --prefix-from value\n", ctx->cbm_prefix_from); + } + dict_data = ctx->unpacked_data + ctx->cbm_prefix_from - ctx->cbm_orig_addr; + dict_size = ctx->cbm_range_from - ctx->cbm_prefix_from; + } + + /* load file from start_pos on only, so skip bytes on input */ + ctx->unpacked_data += (ctx->cbm_range_from - ctx->cbm_orig_addr); + /* also adopt ctx->unpacked_size */ + ctx->unpacked_size -= (ctx->cbm_range_from - ctx->cbm_orig_addr); + /* and set up new load-address */ + ctx->cbm_orig_addr = ctx->cbm_range_from; + + if (ctx->unpacked_size <= 0) { + fprintf(stderr, "Error: Input too small\n"); + exit(1); + } + + printf("Compressing from $%04x to $%04x = $%04lx bytes\n", ctx->cbm_range_from, ctx->cbm_range_to, ctx->unpacked_size); + + if (ctx->cbm_relocate_packed_addr >= 0 || ctx->sfx) { + ctx->inplace = FALSE; + } + + /* write clamped raw data */ + ctx->clamped_name = (char*)malloc(sizeof(src_name)); + strcpy(ctx->clamped_name, src_name); + sfp = fdopen(mkstemp(ctx->clamped_name),"wb"); + if (!sfp) { + fprintf(stderr, "Error: Cannot create clamped file %s\n", ctx->clamped_name); + exit(1); + } + if (ctx->unpacked_size != 0) { + if (fwrite(ctx->unpacked_data, sizeof(char), ctx->unpacked_size, sfp) != ctx->unpacked_size) { + fprintf(stderr, "Error: Cannot write clamped file\n"); + perror("fwrite"); + exit(1); + } + } + fclose(sfp); + + /* ctreate temp file for dict */ + if (ctx->cbm_prefix_from >= 0) { + if (ctx->prefix_name == NULL) { + ctx->prefix_name = (char*)malloc(sizeof(tmp_name)); + strcpy(ctx->prefix_name, tmp_name); + dfp = fdopen(mkstemp(ctx->prefix_name),"wb"); + printf("using prefix: $%04x - $%04x\n", ctx->cbm_prefix_from, ctx->cbm_prefix_from + dict_size); + if (!dfp) { + fprintf(stderr, "Error: Cannot create dict file %s\n", ctx->prefix_name); + exit(1); + } + if (!dict_data || fwrite(dict_data, sizeof(char), dict_size, dfp) != dict_size) { + fprintf(stderr, "Error: Cannot write dict file %s\n", ctx->prefix_name); + remove(ctx->prefix_name); + exit(1); + } + dict_temp = TRUE; + fclose(dfp); + } + } + + /* compress data with salvador */ + salvador_argv[salvador_argc++] = "salvador"; + if (ctx->prefix_name) { + salvador_argv[salvador_argc++] = "-D"; + salvador_argv[salvador_argc++] = ctx->prefix_name; + } + salvador_argv[salvador_argc++] = ctx->clamped_name; + salvador_argv[salvador_argc++] = ctx->output_name; + salvador_main(salvador_argc, salvador_argv); + + /* delete dict */ + if (dict_temp) remove(ctx->prefix_name); + /* remove clamped */ + remove(ctx->clamped_name); + + /* read packed data */ + pfp = fopen(ctx->output_name, "rb"); + if (!pfp) { + fprintf(stderr, "Error: Cannot access input file\n"); + exit(1); + } + ctx->packed_size = fread(ctx->packed_data, sizeof(char), BUFFER_SIZE, pfp); + fclose(pfp); + + /* determine size without eof-marker -> remove 18 bits, either 2 byte or three byte depending on position of last bitpair */ + if (ctx->packed_data[ctx->packed_size - 1] & 0x80) ctx->packed_size -= 3; + else ctx->packed_size -= 2; + + reencode_packed_stream(ctx); + + if (ctx->packed_index + 2 > ctx->unpacked_size) { + fprintf(stderr, "Error: Packed file larger than original\n"); + exit(1); + } + + //printf("control-bytes: $%04x\n", ctx->lz_bits); + + write_reencoded_stream(ctx); + return; +} + +int main(int argc, char *argv[]) { + int i; + + ctx ctx = { 0 }; + + ctx.output_name = NULL; + ctx.input_name = NULL; + ctx.prefix_name = NULL; + + ctx.inplace = TRUE; + + ctx.cbm = TRUE; + ctx.cbm_orig_addr = 0; + ctx.cbm_packed_addr = 0; + ctx.cbm_range_from = -1; + ctx.cbm_range_to = -1; + ctx.cbm_relocate_packed_addr = -1; + ctx.cbm_relocate_origin_addr = -1; + ctx.cbm_prefix_from = -1; + + ctx.sfx = FALSE; + ctx.sfx_addr = -1; + ctx.sfx_01 = -1; + ctx.sfx_cli = FALSE; + ctx.sfx_small = FALSE; + ctx.sfx_code = NULL; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "-", 1) || !strncmp(argv[i], "--", 2)) { + if (!strcmp(argv[i], "--binfile")) { + ctx.cbm = FALSE; + } else if (!strcmp(argv[i], "--prefix-from")) { + ctx.cbm_prefix_from = read_number(argv[i + 1], argv[i], 65536); + i++; + } else if (!strcmp(argv[i], "--prefix-file")) { + i++; + ctx.prefix_name = argv[i]; + } else if (!strcmp(argv[i], "--no-inplace")) { + ctx.inplace = FALSE; + } else if (!strcmp(argv[i], "--small")) { + ctx.sfx_small = TRUE; + } else if (!strcmp(argv[i], "--relocate-packed")) { + ctx.cbm_relocate_packed_addr = read_number(argv[i + 1], argv[i], 65536); + i++; + } else if (!strcmp(argv[i], "--relocate-origin")) { + ctx.cbm_relocate_origin_addr = read_number(argv[i + 1], argv[i], 65536); + i++; + } else if (!strcmp(argv[i], "--from")) { + ctx.cbm_range_from = read_number(argv[i + 1], argv[i], 65536); + i++; + } else if (!strcmp(argv[i], "--to")) { + ctx.cbm_range_to = read_number(argv[i + 1], argv[i], 65536); + i++; + } else if (!strcmp(argv[i], "--01")) { + ctx.sfx_01 = read_number(argv[i + 1], argv[i], 256); + i++; + } else if (!strcmp(argv[i], "--cli")) { + ctx.sfx_cli = TRUE; + } else if (!strcmp(argv[i], "--sfx")) { + ctx.sfx_addr = read_number(argv[i + 1], argv[i], 65536); + i++; + ctx.sfx = TRUE; + ctx.inplace = FALSE; + } else if (!strcmp(argv[i], "-o")) { + i++; + ctx.output_name = argv[i]; + } else { + fprintf(stderr, "Error: Unknown option %s\n", argv[i]); + exit(1); + } + } else if (i == argc - 1) { + ctx.input_name = argv[i]; + } else { + fprintf(stderr, "Error: Unknown option %s\n", argv[i]); + exit(1); + } + } + + printf("dali v0.3 - a zx0-reencoder for bitfire by Tobias Bindhammer\n"); + printf("underlying zx0-packer salvador by Emmanuel Marty\n"); + + if (argc == 1) { + fprintf(stderr, "Usage: %s [options] input\n" + " -o [filename] Set output filename.\n" + " --sfx [num] Create a c64 compatible sfx-executable.\n" + " --01 [num] Set 01 to [num] after sfx.\n" + " --cli [num] Do a CLI after sfx, default is SEI.\n" + " --small Use a very small depacker that fits into zeropage, but --01 and --cli are ignored and it trashes zeropage (!)\n" + " --no-inplace Disable inplace-decompression.\n" + " --binfile Input file is a raw binary without load-address.\n" + " --from [num] Compress file from [num] on.\n" + " --to [num] Compress file until position [num].\n" + " --prefix-from [num] Use preceeding data from [num] on as dictionary (in combination with --from).\n" + " --prefix-file [file] Use preceeding data from [file] as dictionary.\n" + " --relocate-packed [num] Relocate packed data to desired address [num] (resulting file can't de decompressed inplace!)\n" + " --relocate-origin [num] Set load-address of source file to [num] prior to compression. If used on bin-files, load-address and depack-target is prepended on output.\n" + ,argv[0]); + exit(1); + } + + if (!ctx.sfx && ctx.sfx_small) { + fprintf(stderr, "Info: No sfx, ignoring --small option\n"); + } + if (!ctx.sfx && ctx.sfx_01 >= 0) { + fprintf(stderr, "Info: No sfx, ignoring --01 option\n"); + } + if (!ctx.sfx && ctx.sfx_cli) { + fprintf(stderr, "Info: No sfx, ignoring --cli option\n"); + } + + if (ctx.input_name == NULL) { + fprintf(stderr, "Error: No input-filename given\n"); + exit(1); + } + + do_reencode(&ctx); + return 0; +} diff --git a/loader/tools/dali/depack.asm b/loader/tools/dali/depack.asm new file mode 100644 index 0000000..2903fe7 --- /dev/null +++ b/loader/tools/dali/depack.asm @@ -0,0 +1,106 @@ +!cpu 6510 + * = $1000 + + sei + lda #$35 + sta $01 + lda $d011 + bpl *-3 + lda #$0b + sta $d011 + + ldx #$00 + lda #$20 +- + sta $0400,x + sta $0500,x + sta $0600,x + sta $0700,x + dex + bne - + + jsr .timer_start + + ldx #data_start + + jsr depack + + jsr .timer_stop + + lda #$1b + sta $d011 + jmp * + +.timer_start + lda #$00 + sta $dc0e + lda #$40 + sta $dc0f + lda #$ff + sta $dc04 + sta $dc05 + sta $dc06 + sta $dc07 + lda #$41 + sta $dc0f + lda #$01 + sta $dc0e + rts +.timer_stop + lda #$00 + sta $dc0e + lda #$40 + sta $dc0f + + ldy #$00 +- + lda .cycles,y + sta $0400,y + iny + cpy #$08 + bne - + lda $dc04 + pha + lda $dc05 + pha + lda $dc06 + pha + lda $dc07 + jsr .print_hex + pla + jsr .print_hex + pla + jsr .print_hex + pla +.print_hex + eor #$ff + pha + lsr + lsr + lsr + lsr + tax + lda .hextab,x + sta $0400,y + iny + pla + ldx #$0f + sbx #$00 + lda .hextab,x + sta $0400,y + iny + rts +.cycles + !scr "cycles: " +.hextab + !scr "0123456789abcdef" + +!align 255,0 +depack +!src "dzx0_dali.asm" +!warn "depacker size: ", * - depack + +; * = $6a61 +data_start +!bin "testfile.lz",,4 diff --git a/loader/tools/dali/dzx0_dali.asm b/loader/tools/dali/dzx0_dali.asm new file mode 100644 index 0000000..03f7712 --- /dev/null +++ b/loader/tools/dali/dzx0_dali.asm @@ -0,0 +1,282 @@ +!cpu 6510 + +CONFIG_ZP_ADDR = $f0 +LZ_BITS_LEFT = 0 +INPLACE = 0 +SETUP_LZ_DST = 0 + +lz_bits = CONFIG_ZP_ADDR + 0 +lz_dst = CONFIG_ZP_ADDR + 1 +lz_src = CONFIG_ZP_ADDR + 3 +lz_len_hi = CONFIG_ZP_ADDR + 5 + +!macro get_lz_bit { + !if LZ_BITS_LEFT = 1 { + asl = dst XXX TODO +} else { +.lz_end_check +} +.lz_start_over + lda #$01 ;we fall through this check on entry and start with literal + +get_lz_bit + bcs .lz_match ;after each match check for another match or literal? + + ;------------------ + ;LITERAL + ;------------------ +.lz_literal + +get_lz_bit + bcs + +- ;lz_length as inline + +get_lz_bit ;fetch payload bit + rol ;can also moved to front and executed once on start + +get_lz_bit + bcc - ++ + bne + + jsr .lz_refill_bits ++ + tax +.lz_l_page_ +.lz_cp_lit + lda (lz_src),y ;/!\ Need to copy this way, or we run into danger to copy from an area that is yet blocked by barrier, this totally sucks, loading in order reveals that + sta (lz_dst),y + + inc A = 1 with rol, but not if we copy literal this way + +get_lz_bit + bcs .lz_match ;either match with new offset or old offset + + ;------------------ + ;REPEAT LAST OFFSET + ;------------------ +.lz_repeat + +get_lz_bit ;cheaper with 2 branches, as initial branch to .lz_literal therefore is removed + bcs + +- + +get_lz_bit ;fetch payload bit + rol ;can also moved to front and executed once on start + +get_lz_bit ;cheaper with 2 branches, as initial branch to .lz_literal therefore is removed + bcc - ++ + bne + + jsr .lz_refill_bits ;fetch more bits + beq .lz_m_page ;avoid underflow of A on sbc #$01 faster than forcing carry to 1 with a sec all times ++ + sbc #$01 ;subtract 1, will be added again on adc as C = 1 +.lz_match_big ;we enter with length - 1 here from normal match + eor #$ff + tay +.lz_m_page_ + eor #$ff ;restore A +.lz_match_len2 ;entry from new_offset handling + adc first bit for lenth is in carry, and we have %0xxxxxxx xxxxxxxx as offset + sta .lz_offset_lo + 1 + + inc move away to prefer Z = 0 case + + ;------------------ + ;NEW OR OLD OFFSET + ;------------------ + + lda #$01 + asl <.lz_bits + bcs .lz_new_offset ;either match with new offset or old offset +!if ZX0_INLINE_GET_LEN == 1 { + bcc .lz_match_repeat + + ;------------------ + ;DO MATCH + ;------------------ +- ;lz_get_len as inline + asl <.lz_bits ;fetch payload bit + rol +.lz_match_repeat + asl <.lz_bits + bcc - ++ + bne + + jsr .lz_refill_bits ++ +} else { +.lz_match_repeat + jsr .lz_get_len +} + sbc #$01 ;saves the iny later on + bcs + + dcp .lz_len_hi ;dec highbyte of length by one, a = $ff, so cmp will always set carry for free on top ++ +.lz_match_ + eor #$ff + ;beq .lz_calc_msrc ;just fall through on zero? $ff + sec -> addition is neutralized and carry is set, so no harm + tay + eor #$ff ;restore A +.lz_match__ ;entry from new_offset handling + adc <.lz_dst + 0 + sta <.lz_dst + 0 + bcs .lz_clc ;/!\ branch happens very seldom + dec <.lz_dst + 1 +.lz_clc + clc +.lz_offset_lo adc #$00 ;carry is cleared, subtract (offset + 1) + sta .lz_msrcr + 0 + lda <.lz_dst + 1 +.lz_offset_hi adc #$ff + sta .lz_msrcr + 1 +.lz_cp_match +.lz_msrcr = * + 1 + lda $beef,y + sta (.lz_dst),y + iny + bne .lz_cp_match + inc <.lz_dst + 1 + + lda <.lz_len_hi ;check for more loop runs +!if ZX0_INPLACE == 1 { + bne .lz_m_page ;do more page runs + + cpx <.lz_dst + 0 ;check for end condition when depacking inplace + bne .lz_start_over + lda <.lz_dst + 1 + sbc <.lz_src1 + bne .lz_start_over + rts +} else { + beq .lz_start_over ;do more page runs +} + ;------------------ + ;SELDOM STUFF + ;------------------ +.lz_m_page + dec <.lz_len_hi + inc .lz_msrcr + 1 + jmp .lz_cp_match +.lz_l_page + dec <.lz_len_hi + sec ;only needs to be set for consecutive rounds of literals, happens very seldom + ldy #$00 + beq .lz_cp_literal + + ;------------------ + ;FETCH A NEW OFFSET + ;------------------ + +!if ZX0_INLINE_GET_LEN == 1 { +- ;lz_get_len as inline + asl <.lz_bits ;fetch payload bit + rol +.lz_new_offset + asl <.lz_bits + bcc - ++ + bne + + jsr .lz_refill_bits ++ +} else { +.lz_new_offset + jsr .lz_get_len +} + sbc #$01 + bcc .lz_eof ;underflow. must have been 0 + eor #$ff + + ror + sta .lz_offset_hi + 1 ;hibyte of offset + +.lz_src2 = * + 2 + lda $1000,x ;looks expensive, but is cheaper than loop + inx + bne + + jsr .lz_inc_src_hi ++ + ror + sta .lz_offset_lo + 1 + + lda #$01 + ldy #$fe + bcs .lz_match__ ;length = 2 ^ $ff, do it the very short way :-) + ldy #$00 +!if ZX0_INLINE_GET_LEN == 1 { +- + asl <.lz_bits ;fetch first payload bit + rol + asl <.lz_bits + bcc - + bne .lz_match_ + jsr .lz_refill_bits ;fetch remaining bits +} else { + jsr .lz_get_len_ +} + bcs .lz_match_ + +.lz_inc_src_hi + inc .lz_src1 + inc .lz_src2 + inc .lz_src3 + rts + +!if ZX0_INLINE_GET_LEN == 0 { +.lz_get_len_ +- ;lz_get_len as inline + asl <.lz_bits ;fetch payload bit + rol +.lz_get_len + asl <.lz_bits + bcc - + bne .lz_get_end +} +.lz_refill_bits ;refill bits, this happens after 4 payload-bits bestcase +.lz_src3 = * + 2 + ldy $1000,x + inx + bne + + jsr .lz_inc_src_hi ++ + sty <.lz_bits + ldy #$00 + rol <.lz_bits + bcs .lz_get_end +- ;continue with 16 bit shifting + asl <.lz_bits ;fetch payload bit + rol ;can also moved to front and executed once on start +.lz_get_len_16 + rol <.lz_len_hi + asl <.lz_bits + bcc - + beq .lz_refill_bits +.lz_get_end +.lz_eof + rts +.depacker_end diff --git a/loader/tools/dali/dzx0_orig_zx0_v2.asm b/loader/tools/dali/dzx0_orig_zx0_v2.asm new file mode 100644 index 0000000..170c3f4 --- /dev/null +++ b/loader/tools/dali/dzx0_orig_zx0_v2.asm @@ -0,0 +1,207 @@ +;/!\ Attention, this depacker only works with the original zx0 version contained within this folder as well, it does not work with the modified version coming with bitfire, as some things on the encoding got changed due to speed optimizations + +!cpu 6510 + +;ZX0_INPLACE = 0 + +.ZP_ADDR = $f8 +.lz_dst = .ZP_ADDR + 0 +.lz_bits = .ZP_ADDR + 2 +.lz_len_hi = .ZP_ADDR + 4 + +.depacker_start + ;------------------ + ;INIT + ;------------------ + + ;lowbyte of data start must be in X, highbyte in A + sta .lz_src1 + sta .lz_src2 + sta .lz_src3 + + lda #$00 + sta <.lz_dst + 0 + lda #$a0 + sta <.lz_dst + 1 + + ldy #$ff + sty .lz_offset_lo + 1 + sty .lz_offset_hi + 1 + + iny + sty <.lz_len_hi + lda #$40 + sta <.lz_bits ;will make us fall through on next test and force us to load a new byte into bit-buffer upon next .lz_get_len + + ;------------------ + ;LITERAL + ;------------------ +.lz_start_over + lda #$01 + asl <.lz_bits + bcs .lz_new_offset +.lz_literal + jsr .lz_get_len + sta .lz_y + 1 + and #$ff ;annoying, but flags are not set corresponding to A + beq .lz_l_page_ +.lz_cp_literal +.lz_src1 = * + 2 + lda $1000,x + inx + bne + + jsr .lz_inc_src_hi ++ + sta (.lz_dst),y + iny +.lz_y cpy #$00 + bne .lz_cp_literal + + dey ;this way we force increment of lz_dst + 1 if y = 0 + tya ;carry is still set on first round + adc <.lz_dst + 0 ;correct dst after copy loop + sta <.lz_dst + 0 + bcc + + inc <.lz_dst + 1 ++ + ldy <.lz_len_hi + bne .lz_l_page ;happens very seldom -> move away to prefer Z = 0 case + + ;------------------ + ;NEW OR OLD OFFSET + ;------------------ + + lda #$01 + asl <.lz_bits + bcs .lz_new_offset ;either match with new offset or old offset +.lz_match_repeat + jsr .lz_get_len +;!if ZX0_INPLACE == 0 { +;} + sbc #$01 ;saves the iny later on + bcc .lz_dcp ;dec highbyte of length by one, a = $ff, so cmp will always set carry for free on top +.lz_match_ + eor #$ff + ;beq .lz_calc_msrc ;just fall through on zero? $ff + sec -> addition is neutralized and carry is set, so no harm + tay + eor #$ff ;restore A +.lz_match__ ;entry from new_offset handling + adc <.lz_dst + 0 + sta <.lz_dst + 0 + bcs .lz_clc ;/!\ branch happens very seldom + dec <.lz_dst + 1 +.lz_clc_back + clc +.lz_offset_lo adc #$ff ;carry is cleared, subtract (offset + 1) + sta .lz_msrcr + 0 + lda <.lz_dst + 1 +.lz_offset_hi adc #$ff + sta .lz_msrcr + 1 +.lz_cp_match +.lz_msrcr = * + 1 + lda $beef,y + sta (.lz_dst),y + iny + bne .lz_cp_match + inc <.lz_dst + 1 + + lda <.lz_len_hi ;check for more loop runs +;!if ZX0_INPLACE == 1 { +; bne .lz_m_page ;do more page runs +; +; cpx <.lz_dst + 0 ;check for end condition when depacking inplace +; bne .lz_start_over +; lda <.lz_dst + 1 +; sbc <.lz_src1 +; bne .lz_start_over +; rts +;.lz_m_page +; lda #$ff +;} else { + beq .lz_start_over ;do more page runs + lda #$ff +;} + ;------------------ + ;SELDOM STUFF + ;------------------ +.lz_dcp + dcp .lz_len_hi + bcs .lz_match_ +.lz_clc + clc + bcc .lz_clc_back +.lz_l_page + sec ;only needs to be set for consecutive rounds of literals, happens very seldom + ldy #$00 +.lz_l_page_ + dec <.lz_len_hi + bcs .lz_cp_literal + + ;------------------ + ;FETCH A NEW OFFSET + ;------------------ + +.lz_new_offset + lda #$fe + jsr .lz_get_len + sty .lz_len_hi + adc #$00 + beq .lz_eof ;underflow. must have been 0 + + sec + ror + sta .lz_offset_hi + 1 ;hibyte of offset + +.lz_src2 = * + 2 + lda $1000,x ;looks expensive, but is cheaper than loop + inx + bne + + jsr .lz_inc_src_hi ++ + ror + sta .lz_offset_lo + 1 + + lda #$01 + ldy #$fe + bcs .lz_match__ ;length = 2 ^ $ff, do it the very short way :-) + ldy #$00 + jsr .lz_get_len_ + bcs .lz_match_ + +.lz_inc_src_hi + inc .lz_src1 + inc .lz_src2 + inc .lz_src3 + rts + +.lz_get_len_ +- ;lz_get_len as inline + asl <.lz_bits ;fetch payload bit + rol +.lz_get_len + asl <.lz_bits + bcc - + bne .lz_get_end +.lz_refill_bits ;refill bits, this happens after 4 payload-bits bestcase +.lz_src3 = * + 2 + ldy $1000,x + inx + bne + + jsr .lz_inc_src_hi ++ + sty <.lz_bits + ldy #$00 + rol <.lz_bits + bcs .lz_get_end +- ;continue with 16 bit shifting + asl <.lz_bits ;fetch payload bit + rol ;can also moved to front and executed once on start +.lz_get_len_16 + rol <.lz_len_hi + asl <.lz_bits + bcc - + beq .lz_refill_bits +.lz_get_end +.lz_eof + rts +.depacker_end diff --git a/loader/tools/dali/salvador/LICENSE b/loader/tools/dali/salvador/LICENSE new file mode 100644 index 0000000..4cc4c04 --- /dev/null +++ b/loader/tools/dali/salvador/LICENSE @@ -0,0 +1,3 @@ +The salvador code is available under the Zlib license, except for src/matchfinder.c which is placed under the Creative Commons CC0 license. + +Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information. diff --git a/loader/tools/dali/salvador/LICENSE.cc0.md b/loader/tools/dali/salvador/LICENSE.cc0.md new file mode 100644 index 0000000..139c68e --- /dev/null +++ b/loader/tools/dali/salvador/LICENSE.cc0.md @@ -0,0 +1,43 @@ +## creative commons + +# CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +### Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. __Limitations and Disclaimers.__ + + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/loader/tools/dali/salvador/LICENSE.zlib.md b/loader/tools/dali/salvador/LICENSE.zlib.md new file mode 100644 index 0000000..d27121a --- /dev/null +++ b/loader/tools/dali/salvador/LICENSE.zlib.md @@ -0,0 +1,19 @@ +Copyright (c) 2021 Emmanuel Marty + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/loader/tools/dali/salvador/Makefile b/loader/tools/dali/salvador/Makefile new file mode 100644 index 0000000..c09b0e9 --- /dev/null +++ b/loader/tools/dali/salvador/Makefile @@ -0,0 +1,28 @@ +CC=clang +CFLAGS=-O3 -g -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc +OBJDIR=obj +LDFLAGS= + +$(OBJDIR)/%.o: src/../%.c + @mkdir -p '$(@D)' + $(CC) $(CFLAGS) -c $< -o $@ + +APP := salvador + +OBJS += $(OBJDIR)/src/salvador.o +OBJS += $(OBJDIR)/src/expand.o +OBJS += $(OBJDIR)/src/matchfinder.o +OBJS += $(OBJDIR)/src/shrink.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort_utils.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/sssort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/trsort.o + +all: $(APP) + +$(APP): $(OBJS) + $(CC) $^ $(LDFLAGS) -o $(APP) + +clean: + @rm -rf $(APP) $(OBJDIR) + diff --git a/loader/tools/dali/salvador/README.md b/loader/tools/dali/salvador/README.md new file mode 100644 index 0000000..a50c40c --- /dev/null +++ b/loader/tools/dali/salvador/README.md @@ -0,0 +1,31 @@ +salvador -- a fast, near-optimal compressor for the ZX0 format +============================================================== + +salvador is a command-line tool and a library that compresses bitstreams in the ZX0 format. + +The tool outputs compressed files that are within 0.02% on average, of the files produced by the zx0 packer itself. The compressor is, however, several orders of magnitude faster, with compression speed similar to [apultra](https://github.com/emmanuel-marty/apultra). + +The compressor can pack files of any size, however, due to the 31.5 KB window size, files larger than 128-256 KB will get a better ratio with apultra. This will not be an issue when compressing for the main target, 8-bit micros. By default, salvador compresses for the modern (V2) format. The classic, legacy format is also supported; use the -classic flag on the command line. + +salvador is written in portable C. It is fully open-source under a liberal license. You can use the ZX0 decompression libraries for your target environment. As with LZSA and apultra, you can do whatever you like with it. + +The output is fully compatible with the [ZX0](https://github.com/einar-saukas/ZX0) compressor by Einar Saukas. + +Check out [Dali](https://csdb.dk/release/?id=213694&show=summary) by Bitbreaker, that uses Salvador to compress for the C64, including self, in-place decompression and proper handling of load-addresses. The tool is part of the [Bitfire](https://github.com/bboxy/bitfire) C64 loading system. + +Included 8-bit decompression code: + + * [8088](https://github.com/emmanuel-marty/salvador/tree/main/asm/8088) by Emmanuel Marty. + * [68000](https://github.com/emmanuel-marty/salvador/tree/main/asm/68000) by Emmanuel Marty. + * [z80](https://github.com/emmanuel-marty/salvador/tree/main/asm/Z80) by spke and uniabis. Use the -classic flag to compress data for the Z80. + * [6502](https://github.com/emmanuel-marty/salvador/tree/main/asm/6502) by John Brandwood. Use the -classic flag to compress data for the 6502. + * [HuC6280](https://github.com/emmanuel-marty/salvador/tree/main/asm/HuC6280) by John Brandwood. Use the -classic flag for this platform as well. + +External decompression code: + + * [6809 and 6309](https://github.com/dougmasten/zx0-6x09) by Doug Masten. Use the -classic flag to compress data for the 6809 or 6309 depackers. + +License: + +* The salvador code is available under the Zlib license. +* The match finder (matchfinder.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder. diff --git a/loader/tools/dali/salvador/VS2019/salvador.sln b/loader/tools/dali/salvador/VS2019/salvador.sln new file mode 100644 index 0000000..e3eb04b --- /dev/null +++ b/loader/tools/dali/salvador/VS2019/salvador.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31729.503 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "salvador", "salvador.vcxproj", "{F4C10DBA-8808-4418-A78F-719C6A7761EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Debug|x64.ActiveCfg = Debug|x64 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Debug|x64.Build.0 = Debug|x64 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Debug|x86.ActiveCfg = Debug|Win32 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Debug|x86.Build.0 = Debug|Win32 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Release|x64.ActiveCfg = Release|x64 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Release|x64.Build.0 = Release|x64 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Release|x86.ActiveCfg = Release|Win32 + {F4C10DBA-8808-4418-A78F-719C6A7761EF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6A45AD4E-9B6B-4AAB-9FEA-CA453AE1822D} + EndGlobalSection +EndGlobal diff --git a/loader/tools/dali/salvador/VS2019/salvador.vcxproj b/loader/tools/dali/salvador/VS2019/salvador.vcxproj new file mode 100644 index 0000000..7e4fb81 --- /dev/null +++ b/loader/tools/dali/salvador/VS2019/salvador.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f4c10dba-8808-4418-a78f-719c6a7761ef} + salvador + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + false + $(ProjectDir)bin\ + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + false + $(ProjectDir)bin\ + + + + Level3 + true + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\src\libdivsufsort\include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\src\libdivsufsort\include;%(AdditionalIncludeDirectories) + Speed + true + MultiThreaded + + + Console + true + true + true + + + + + Level3 + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\src\libdivsufsort\include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\src\libdivsufsort\include;%(AdditionalIncludeDirectories) + Speed + true + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/loader/tools/dali/salvador/VS2019/salvador.vcxproj.filters b/loader/tools/dali/salvador/VS2019/salvador.vcxproj.filters new file mode 100644 index 0000000..607fa47 --- /dev/null +++ b/loader/tools/dali/salvador/VS2019/salvador.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {86e66d4d-937b-4037-af93-856105377549} + + + {c4baa29b-3731-40b1-bcc9-d5aa5673114f} + + + {56092ba4-e514-4de2-8528-adff2da7ac3f} + + + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources\libdivsufsort\include + + + \ No newline at end of file diff --git a/loader/tools/dali/salvador/VS2019/salvador.vcxproj.user b/loader/tools/dali/salvador/VS2019/salvador.vcxproj.user new file mode 100644 index 0000000..217273f --- /dev/null +++ b/loader/tools/dali/salvador/VS2019/salvador.vcxproj.user @@ -0,0 +1,19 @@ + + + + -c -v -test + WindowsLocalDebugger + + + -c -v -test + WindowsLocalDebugger + + + -c -v -test + WindowsLocalDebugger + + + -c -v -test + WindowsLocalDebugger + + \ No newline at end of file diff --git a/loader/tools/dali/salvador/asm/6502/zx0_6502.asm b/loader/tools/dali/salvador/asm/6502/zx0_6502.asm new file mode 100644 index 0000000..ff0e368 --- /dev/null +++ b/loader/tools/dali/salvador/asm/6502/zx0_6502.asm @@ -0,0 +1,251 @@ +; *************************************************************************** +; *************************************************************************** +; +; zx0_6502.asm +; +; NMOS 6502 decompressor for data stored in Einar Saukas's ZX0 format. +; +; This code is written for the ACME assembler. +; +; The code is 196 bytes long, and is self-modifying. +; +; Copyright John Brandwood 2021. +; +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) +; +; *************************************************************************** +; *************************************************************************** + + + +; *************************************************************************** +; *************************************************************************** +; +; Decompression Options & Macros +; + + ; + ; Assume that we're decompessing from a large multi-bank + ; compressed data file, and that the next bank may need to + ; paged in when a page-boundary is crossed. + ; + +ZX0_FROM_BANK = 0 + + ; + ; Macro to increment the source pointer to the next page. + ; + + !macro ZX0_INC_PAGE { + !if ZX0_FROM_BANK { + jsr zx0_next_page + } else { + inc > 8 ; Default to a 2KB window in +ZX0_WINMSK = ($0800 - 1) >> 8 ; RAM, located at $3800. + + .endif + + + +; *************************************************************************** +; *************************************************************************** +; +; Data usage is 11 bytes of zero-page, using aliases for clarity. +; + +zx0_srcptr = __si ; 1 word. +zx0_dstptr = __di ; 1 word. + +zx0_length = __ax ; 1 word. +zx0_offset = __bx ; 1 word. +zx0_winptr = __cx ; 1 word. +zx0_bitbuf = __dl ; 1 byte. + + + +; *************************************************************************** +; *************************************************************************** +; +; zx0_to_ram - Decompress data stored in Einar Saukas's ZX0 "classic" format. +; +; Args: __si, __si_bank = _farptr to compressed data in MPR3. +; Args: __di = ptr to output address in RAM. +; +; Uses: __si, __di, __ax, __bx, __cx, __dh ! +; + +zx0_to_ram .proc + + jsr __si_to_mpr3 ; Map zx0_srcptr to MPR3. + + ldx #$40 ; Initialize bit-buffer. + + ldy #$FF ; Initialize offset to $FFFF. + sty = run of literals, or + ; 1 + + [7-bits of offset lsb + 1-bit of length] + = another match +AfterMatch1 add a : jr nc,RunOfLiterals + +UsualMatch: ; this is the case of usual match+offset + add a : jr nc,LongerOffets : jr nz,ShorterOffsets ; NZ after NC == "confirmed C" + RELOAD_BITS : jr c,ShorterOffsets + +LongerOffets inc c : INLINE_READ_GAMMA ; reading gamma requires C=1 + call z,ReloadReadGamma + +ProcessOffset exa : xor a : sub c + ret z ; end-of-data marker (only checked for longer offsets) + + rra : ld (PrevOffset+1),a + ld a,(hl) : inc hl + rra : ld (PrevOffset),a + + ; lowest bit is the first bit of the gamma code for length + jr c,CopyMatch2 + + ; this wastes 1 t-state for longer matches far away, + ; but saves 4 t-states for longer nearby (seems to pay off in testing) + ld c,b +LongerMatch inc c + ; doing SCF here ensures that AF' has flag C ON and costs + ; cheaper than doing SCF in the ShortestOffsets branch + scf : exa + + INLINE_READ_GAMMA + call z,ReloadReadGamma + +CopyMatch3 push hl ; preserve source + ld hl,(PrevOffset) ; restore offset + add hl,de ; HL = dest - offset + ; because BC>=3-1, we can do 2 x LDI safely + ldi : ldir : inc c : ldi + pop hl ; restore source + + ; after a match you can have either + ; 0 + = run of literals, or + ; 1 + + [7-bits of offset lsb + 1-bit of length] + = another match +AfterMatch3 add a : jr c,UsualMatch + +RunOfLiterals: inc c : add a : jr nc,LongerRun : jr nz,CopyLiteral ; NZ after NC == "confirmed C" + RELOAD_BITS : jr c,CopyLiteral + +LongerRun INLINE_READ_GAMMA : jr nz,CopyLiterals + RELOAD_BITS + call nc,ReadGammaAligned + +CopyLiterals ldi +CopyLiteral ldir + + ; after a literal run you can have either + ; 0 + = match using a repeated offset, or + ; 1 + + [7-bits of offset lsb + 1-bit of length] + = another match + add a : jr c,UsualMatch + +RepMatch: inc c : add a : jr nc,LongerRepMatch : jr nz,CopyMatch1 ; NZ after NC == "confirmed C" + RELOAD_BITS : jr c,CopyMatch1 + +LongerRepMatch INLINE_READ_GAMMA + jp nz,CopyMatch1 + + ; this is a crafty equivalent of + ; CALL ReloadReadGamma : JP CopyMatch1 + push ix + +; +; the subroutine for reading the remainder of the partly read Elias gamma code. +; it has two entry points: ReloadReadGamma first refills the bit reservoir in A, +; while ReadGammaAligned assumes that the bit reservoir has just been refilled. + +ReloadReadGamma: RELOAD_BITS + ret c + +ReadGammaAligned: add a : rl c + add a : ret c + + add a : rl c +ReadingLongGammaRLA rla ; this should really be an ADD A, but since flag C + ; is always off here, this saves us a byte (see below) + +ReadingLongGamma ; this loop does not need unrolling, + ; as it does not get much use anyway + ret c + add a : rl c : rl b + add a : jr nz,ReadingLongGamma + + ld a,(hl) : inc hl + jr ReadingLongGammaRLA + diff --git a/loader/tools/dali/salvador/src/expand.c b/loader/tools/dali/salvador/src/expand.c new file mode 100644 index 0000000..80ed7f1 --- /dev/null +++ b/loader/tools/dali/salvador/src/expand.c @@ -0,0 +1,336 @@ +/* + * expand.c - decompressor implementation + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include +#include +#include "format.h" +#include "expand.h" +#include "libsalvador.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE int salvador_read_bit(const unsigned char **ppInBlock, const unsigned char *pDataEnd, int *nCurBitMask, unsigned char *bits) { + int nBit; + + const unsigned char* pInBlock = *ppInBlock; + + if ((*nCurBitMask) == 0) { + if (pInBlock >= pDataEnd) return -1; + (*bits) = *pInBlock++; + (*nCurBitMask) = 128; + } + + nBit = ((*bits) & 128) ? 1 : 0; + + (*bits) <<= 1; + (*nCurBitMask) >>= 1; + + *ppInBlock = pInBlock; + return nBit; +} + +static inline FORCE_INLINE int salvador_read_elias(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, const int nIsBackward, int* nCurBitMask, unsigned char* bits) { + int nValue = nInitialValue; + + if (nIsBackward) { + while (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) == 1) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + else { + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + + return nValue; +} + +static inline FORCE_INLINE int salvador_read_elias_inverted(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, int* nCurBitMask, unsigned char* bits) { + int nValue = nInitialValue; + + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) ^ 1); + } + + return nValue; +} + +static inline FORCE_INLINE int salvador_read_elias_prefix(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, const int nIsBackward, int* nCurBitMask, unsigned char* bits, unsigned int nFirstBit) { + int nValue = nInitialValue; + + if (nIsBackward) { + if (nFirstBit) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + while (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) == 1) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + } + else { + if (!nFirstBit) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + } + + return nValue; +} + +/** + * Get maximum decompressed size of compressed data + * + * @param pInputData compressed data + * @param nInputSize compressed size in bytes + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return maximum decompressed size + */ +size_t salvador_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags) { + const unsigned char* pInputDataEnd = pInputData + nInputSize; + int nCurBitMask = 0; + unsigned char bits = 0; + int nMatchOffset = 1; + int nIsFirstCommand = 1; + const int nIsInverted = (nFlags & FLG_IS_INVERTED) && !(nFlags & FLG_IS_BACKWARD); + const int nIsBackward = (nFlags & FLG_IS_BACKWARD) ? 1 : 0; + int nDecompressedSize = 0; + + if (pInputData >= pInputDataEnd) + return -1; + + while (1) { + unsigned int nIsMatchWithOffset; + + if (nIsFirstCommand) { + /* The first command is always literals */ + nIsFirstCommand = 0; + nIsMatchWithOffset = 0; + } + else { + /* Read match with offset / literals bit */ + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + if (nIsMatchWithOffset == 0) { + unsigned int nLiterals = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + /* Count literals */ + + if ((pInputData + nLiterals) <= pInputDataEnd) { + pInputData += nLiterals; + nDecompressedSize += nLiterals; + } + else { + return -1; + } + + /* Read match with offset / rep match bit */ + + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + unsigned int nMatchLen; + + if (nIsMatchWithOffset) { + /* Match with offset */ + + unsigned int nMatchOffsetHighByte; + + if (nIsInverted) + nMatchOffsetHighByte = salvador_read_elias_inverted(&pInputData, pInputDataEnd, 1, &nCurBitMask, &bits); + else + nMatchOffsetHighByte = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + if (nMatchOffsetHighByte == 256) + break; + nMatchOffsetHighByte--; + + if (pInputData >= pInputDataEnd) + return -1; + + unsigned int nMatchOffsetLowByte = (unsigned int)(*pInputData++); + if (nIsBackward) + nMatchOffset = (nMatchOffsetHighByte << 7) | (nMatchOffsetLowByte >> 1); + else + nMatchOffset = (nMatchOffsetHighByte << 7) | (127 - (nMatchOffsetLowByte >> 1)); + nMatchOffset++; + + nMatchLen = salvador_read_elias_prefix(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits, nMatchOffsetLowByte & 1); + + nMatchLen += (2 - 1); + } + else { + /* Rep-match */ + + nMatchLen = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + } + + /* Count matched bytes */ + nDecompressedSize += nMatchLen; + } + + return nDecompressedSize; +} + +/** + * Decompress data in memory + * + * @param pInputData compressed data + * @param pOutData buffer for decompressed data + * @param nInputSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return actual decompressed size, or -1 for error + */ +size_t salvador_decompress(const unsigned char *pInputData, unsigned char *pOutData, size_t nInputSize, size_t nMaxOutBufferSize, size_t nDictionarySize, const unsigned int nFlags) { + const unsigned char *pInputDataEnd = pInputData + nInputSize; + unsigned char *pCurOutData = pOutData + nDictionarySize; + const unsigned char *pOutDataEnd = pCurOutData + nMaxOutBufferSize; + int nCurBitMask = 0; + unsigned char bits = 0; + int nMatchOffset = 1; + int nIsFirstCommand = 1; + const int nIsInverted = (nFlags & FLG_IS_INVERTED) && !(nFlags & FLG_IS_BACKWARD); + const int nIsBackward = (nFlags & FLG_IS_BACKWARD) ? 1 : 0; + + if (pInputData >= pInputDataEnd && pCurOutData < pOutDataEnd) + return -1; + + while (1) { + unsigned int nIsMatchWithOffset; + + if (nIsFirstCommand) { + /* The first command is always literals */ + nIsFirstCommand = 0; + nIsMatchWithOffset = 0; + } + else { + /* Read match with offset / literals bit */ + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + if (nIsMatchWithOffset == 0) { + unsigned int nLiterals = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + /* Copy literals */ + + if ((pInputData + nLiterals) <= pInputDataEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInputData, nLiterals); + pInputData += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + + /* Read match with offset / rep match bit */ + + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + unsigned int nMatchLen; + + if (nIsMatchWithOffset) { + /* Match with offset */ + + unsigned int nMatchOffsetHighByte; + + if (nIsInverted) + nMatchOffsetHighByte = salvador_read_elias_inverted(&pInputData, pInputDataEnd, 1, &nCurBitMask, &bits); + else + nMatchOffsetHighByte = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + if (nMatchOffsetHighByte == 256) + break; + nMatchOffsetHighByte--; + + if (pInputData >= pInputDataEnd) + return -1; + + unsigned int nMatchOffsetLowByte = (unsigned int)(*pInputData++); + if (nIsBackward) + nMatchOffset = (nMatchOffsetHighByte << 7) | (nMatchOffsetLowByte >> 1); + else + nMatchOffset = (nMatchOffsetHighByte << 7) | (127 - (nMatchOffsetLowByte >> 1)); + nMatchOffset++; + + nMatchLen = salvador_read_elias_prefix(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits, nMatchOffsetLowByte & 1); + + nMatchLen += (2 - 1); + } + else { + /* Rep-match */ + + nMatchLen = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + } + + /* Copy matched bytes */ + const unsigned char* pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + else { + return -1; + } + } + else { + return -1; + } + } + else { + return -1; + } + } + + return (size_t)(pCurOutData - pOutData) - nDictionarySize; +} diff --git a/loader/tools/dali/salvador/src/expand.h b/loader/tools/dali/salvador/src/expand.h new file mode 100644 index 0000000..6c5d208 --- /dev/null +++ b/loader/tools/dali/salvador/src/expand.h @@ -0,0 +1,69 @@ +/* + * expand.h - decompressor definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _EXPAND_H +#define _EXPAND_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum decompressed size of compressed data + * + * @param pInputData compressed data + * @param nInputSize compressed size in bytes + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return maximum decompressed size + */ +size_t salvador_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags); + +/** + * Decompress data in memory + * + * @param pInputData compressed data + * @param pOutData buffer for decompressed data + * @param nInputSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return actual decompressed size, or -1 for error + */ +size_t salvador_decompress(const unsigned char *pInputData, unsigned char *pOutData, size_t nInputSize, size_t nMaxOutBufferSize, size_t nDictionarySize, const unsigned int nFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_H */ diff --git a/loader/tools/dali/salvador/src/format.h b/loader/tools/dali/salvador/src/format.h new file mode 100644 index 0000000..12a422f --- /dev/null +++ b/loader/tools/dali/salvador/src/format.h @@ -0,0 +1,43 @@ +/* + * format.h - byte stream format definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define MIN_OFFSET 1 +#define MAX_OFFSET 0x7f80 + +#define MAX_VARLEN 0xffff + +#define BLOCK_SIZE 0x10000 + +#define MIN_MATCH_SIZE 1 + +#endif /* _FORMAT_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CHANGELOG.md b/loader/tools/dali/salvador/src/libdivsufsort/CHANGELOG.md new file mode 100644 index 0000000..fe9d004 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CHANGELOG.md @@ -0,0 +1,21 @@ +# libdivsufsort Change Log + +See full changelog at: https://github.com/y-256/libdivsufsort/commits + +## [2.0.1] - 2010-11-11 +### Fixed +* Wrong variable used in `divbwt` function +* Enclose some string variables with double quotation marks in include/CMakeLists.txt +* Fix typo in include/CMakeLists.txt + +## 2.0.0 - 2008-08-23 +### Changed +* Switch the build system to [CMake](http://www.cmake.org/) +* Improve the performance of the suffix-sorting algorithm + +### Added +* OpenMP support +* 64-bit version of divsufsort + +[Unreleased]: https://github.com/y-256/libdivsufsort/compare/2.0.1...HEAD +[2.0.1]: https://github.com/y-256/libdivsufsort/compare/2.0.0...2.0.1 diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeLists.txt b/loader/tools/dali/salvador/src/libdivsufsort/CMakeLists.txt new file mode 100644 index 0000000..7859943 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeLists.txt @@ -0,0 +1,99 @@ +### cmake file for building libdivsufsort Package ### +cmake_minimum_required(VERSION 2.4.4) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +include(AppendCompilerFlags) + +## Project information ## +project(libdivsufsort C) +set(PROJECT_VENDOR "Yuta Mori") +set(PROJECT_CONTACT "yuta.256@gmail.com") +set(PROJECT_URL "https://github.com/y-256/libdivsufsort") +set(PROJECT_DESCRIPTION "A lightweight suffix sorting library") +include(VERSION.cmake) + +## CPack configuration ## +set(CPACK_GENERATOR "TGZ;TBZ2;ZIP") +set(CPACK_SOURCE_GENERATOR "TGZ;TBZ2;ZIP") +include(ProjectCPack) + +## Project options ## +option(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) +option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_DIVSUFSORT64 "Build libdivsufsort64" OFF) +option(USE_OPENMP "Use OpenMP for parallelization" OFF) +option(WITH_LFS "Enable Large File Support" ON) + +## Installation directories ## +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32 or 64)") + +set(CMAKE_INSTALL_RUNTIMEDIR "" CACHE PATH "Specify the output directory for dll runtimes (default is bin)") +if(NOT CMAKE_INSTALL_RUNTIMEDIR) + set(CMAKE_INSTALL_RUNTIMEDIR "${CMAKE_INSTALL_PREFIX}/bin") +endif(NOT CMAKE_INSTALL_RUNTIMEDIR) + +set(CMAKE_INSTALL_LIBDIR "" CACHE PATH "Specify the output directory for libraries (default is lib)") +if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") +endif(NOT CMAKE_INSTALL_LIBDIR) + +set(CMAKE_INSTALL_INCLUDEDIR "" CACHE PATH "Specify the output directory for header files (default is include)") +if(NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") +endif(NOT CMAKE_INSTALL_INCLUDEDIR) + +set(CMAKE_INSTALL_PKGCONFIGDIR "" CACHE PATH "Specify the output directory for pkgconfig files (default is lib/pkgconfig)") +if(NOT CMAKE_INSTALL_PKGCONFIGDIR) + set(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif(NOT CMAKE_INSTALL_PKGCONFIGDIR) + +## Build type ## +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_VERBOSE_MAKEFILE ON) +endif(NOT CMAKE_BUILD_TYPE) + +## Compiler options ## +if(MSVC) + append_c_compiler_flags("/W4" "VC" CMAKE_C_FLAGS) + append_c_compiler_flags("/Oi;/Ot;/Ox;/Oy" "VC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("/openmp" "VC" CMAKE_C_FLAGS) + endif(USE_OPENMP) +elseif(BORLAND) + append_c_compiler_flags("-w" "BCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-Oi;-Og;-Os;-Ov;-Ox" "BCC" CMAKE_C_FLAGS_RELEASE) +else(MSVC) + if(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "GCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "GCC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp" "GCC" CMAKE_C_FLAGS) + endif(USE_OPENMP) + else(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "UNKNOWN" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "UNKNOWN" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp;-openmp;-omp" "UNKNOWN" CMAKE_C_FLAGS) + endif(USE_OPENMP) + endif(CMAKE_COMPILER_IS_GNUCC) +endif(MSVC) + +## Add definitions ## +add_definitions(-DHAVE_CONFIG_H=1 -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + +## Add subdirectories ## +add_subdirectory(pkgconfig) +add_subdirectory(include) +add_subdirectory(lib) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif(BUILD_EXAMPLES) + +## Add 'uninstall' target ## +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake") diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake new file mode 100644 index 0000000..58d3f99 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake @@ -0,0 +1,38 @@ +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +macro(append_c_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_c_compiler_flags) + +macro(append_cxx_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_cxx_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_cxx_compiler_flags) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckLFS.cmake b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckLFS.cmake new file mode 100644 index 0000000..e2b0099 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/CheckLFS.cmake @@ -0,0 +1,109 @@ +## Checks for large file support ## +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) + +macro(check_lfs _isenable) + set(LFS_OFF_T "") + set(LFS_FOPEN "") + set(LFS_FSEEK "") + set(LFS_FTELL "") + set(LFS_PRID "") + + if(${_isenable}) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + -D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 + -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + + check_include_file("sys/types.h" HAVE_SYS_TYPES_H) + check_include_file("inttypes.h" HAVE_INTTYPES_H) + check_include_file("stddef.h" HAVE_STDDEF_H) + check_include_file("stdint.h" HAVE_STDINT_H) + + # LFS type1: 8 <= sizeof(off_t), fseeko, ftello + check_type_size("off_t" SIZEOF_OFF_T) + if(SIZEOF_OFF_T GREATER 7) + check_symbol_exists("fseeko" "stdio.h" HAVE_FSEEKO) + check_symbol_exists("ftello" "stdio.h" HAVE_FTELLO) + if(HAVE_FSEEKO AND HAVE_FTELLO) + set(LFS_OFF_T "off_t") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseeko") + set(LFS_FTELL "ftello") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FSEEKO AND HAVE_FTELLO) + endif(SIZEOF_OFF_T GREATER 7) + + # LFS type2: 8 <= sizeof(off64_t), fopen64, fseeko64, ftello64 + if(NOT LFS_OFF_T) + check_type_size("off64_t" SIZEOF_OFF64_T) + if(SIZEOF_OFF64_T GREATER 7) + check_symbol_exists("fopen64" "stdio.h" HAVE_FOPEN64) + check_symbol_exists("fseeko64" "stdio.h" HAVE_FSEEKO64) + check_symbol_exists("ftello64" "stdio.h" HAVE_FTELLO64) + if(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + set(LFS_OFF_T "off64_t") + set(LFS_FOPEN "fopen64") + set(LFS_FSEEK "fseeko64") + set(LFS_FTELL "ftello64") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + endif(SIZEOF_OFF64_T GREATER 7) + endif(NOT LFS_OFF_T) + + # LFS type3: 8 <= sizeof(__int64), _fseeki64, _ftelli64 + if(NOT LFS_OFF_T) + check_type_size("__int64" SIZEOF___INT64) + if(SIZEOF___INT64 GREATER 7) + check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) + check_symbol_exists("_ftelli64" "stdio.h" HAVE__FTELLI64) + if(HAVE__FSEEKI64 AND HAVE__FTELLI64) + set(LFS_OFF_T "__int64") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "_fseeki64") + set(LFS_FTELL "_ftelli64") + set(LFS_PRID "\"I64d\"") + endif(HAVE__FSEEKI64 AND HAVE__FTELLI64) + endif(SIZEOF___INT64 GREATER 7) + endif(NOT LFS_OFF_T) + + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") + endif(${_isenable}) + + if(NOT LFS_OFF_T) + ## not found + set(LFS_OFF_T "long") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseek") + set(LFS_FTELL "ftell") + set(LFS_PRID "\"ld\"") + endif(NOT LFS_OFF_T) + +endmacro(check_lfs) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/ProjectCPack.cmake b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/ProjectCPack.cmake new file mode 100644 index 0000000..7c105f9 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/ProjectCPack.cmake @@ -0,0 +1,38 @@ +# If the cmake version includes cpack, use it +IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") + SET(CPACK_PACKAGE_VENDOR "${PROJECT_VENDOR}") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") + SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") + SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +# SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_FULL}") + + IF(NOT DEFINED CPACK_SYSTEM_NAME) + SET(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF(NOT DEFINED CPACK_SYSTEM_NAME) + + IF(${CPACK_SYSTEM_NAME} MATCHES Windows) + IF(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR}) + ELSE(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR}) + ENDIF(CMAKE_CL_64) + ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows) + + IF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") + ENDIF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + + SET(CPACK_PACKAGE_CONTACT "${PROJECT_CONTACT}") + IF(UNIX) + SET(CPACK_STRIP_FILES "") + SET(CPACK_SOURCE_STRIP_FILES "") +# SET(CPACK_PACKAGE_EXECUTABLES "ccmake" "CMake") + ENDIF(UNIX) + SET(CPACK_SOURCE_IGNORE_FILES "/CVS/" "/build/" "/\\\\.build/" "/\\\\.svn/" "~$") + # include CPack model once all variables are set + INCLUDE(CPack) +ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") diff --git a/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in new file mode 100644 index 0000000..8366a83 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in @@ -0,0 +1,36 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") + +SET(NUM 0) +FOREACH(file ${files}) + IF(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - found") + SET(UNINSTALL_CHECK_${NUM} 1) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - not found") + SET(UNINSTALL_CHECK_${NUM} 0) + ENDIF(EXISTS "$ENV{DESTDIR}${file}") + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +SET(NUM 0) +FOREACH(file ${files}) + IF(${UNINSTALL_CHECK_${NUM}}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ENDIF(${UNINSTALL_CHECK_${NUM}}) + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +FILE(REMOVE "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") diff --git a/loader/tools/dali/salvador/src/libdivsufsort/LICENSE b/loader/tools/dali/salvador/src/libdivsufsort/LICENSE new file mode 100644 index 0000000..249efa4 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2003 Yuta Mori All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/loader/tools/dali/salvador/src/libdivsufsort/README.md b/loader/tools/dali/salvador/src/libdivsufsort/README.md new file mode 100644 index 0000000..381a188 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/README.md @@ -0,0 +1,140 @@ +# libdivsufsort + +libdivsufsort is a software library that implements a lightweight suffix array construction algorithm. + +## News +* 2015-03-21: The project has moved from [Google Code](http://code.google.com/p/libdivsufsort/) to [GitHub](https://github.com/y-256/libdivsufsort) + +## Introduction +This library provides a simple and an efficient C API to construct a suffix array and a Burrows-Wheeler transformed string from a given string over a constant-size alphabet. +The algorithm runs in O(n log n) worst-case time using only 5n+O(1) bytes of memory space, where n is the length of +the string. + +## Build requirements +* An ANSI C Compiler (e.g. GNU GCC) +* [CMake](http://www.cmake.org/ "CMake") version 2.4.2 or newer +* CMake-supported build tool + +## Building on GNU/Linux +1. Get the source code from GitHub. You can either + * use git to clone the repository + ``` + git clone https://github.com/y-256/libdivsufsort.git + ``` + * or download a [zip file](../../archive/master.zip) directly +2. Create a `build` directory in the package source directory. +```shell +$ cd libdivsufsort +$ mkdir build +$ cd build +``` +3. Configure the package for your system. +If you want to install to a different location, change the -DCMAKE_INSTALL_PREFIX option. +```shell +$ cmake -DCMAKE_BUILD_TYPE="Release" \ +-DCMAKE_INSTALL_PREFIX="/usr/local" .. +``` +4. Compile the package. +```shell +$ make +``` +5. (Optional) Install the library and header files. +```shell +$ sudo make install +``` + +## API +```c +/* Data types */ +typedef int32_t saint_t; +typedef int32_t saidx_t; +typedef uint8_t sauchar_t; + +/* + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array or suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +saint_t +divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n); + +/* + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); +``` + +## Example Usage +```c +#include +#include +#include + +#include + +int main() { + // intput data + char *Text = "abracadabra"; + int n = strlen(Text); + int i, j; + + // allocate + int *SA = (int *)malloc(n * sizeof(int)); + + // sort + divsufsort((unsigned char *)Text, SA, n); + + // output + for(i = 0; i < n; ++i) { + printf("SA[%2d] = %2d: ", i, SA[i]); + for(j = SA[i]; j < n; ++j) { + printf("%c", Text[j]); + } + printf("$\n"); + } + + // deallocate + free(SA); + + return 0; +} +``` +See the [examples](examples) directory for a few other examples. + +## Benchmarks +See [Benchmarks](https://github.com/y-256/libdivsufsort/blob/wiki/SACA_Benchmarks.md) page for details. + +## License +libdivsufsort is released under the [MIT license](LICENSE "MIT license"). +> The MIT License (MIT) +> +> Copyright (c) 2003 Yuta Mori All rights reserved. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +## Author +* Yuta Mori diff --git a/loader/tools/dali/salvador/src/libdivsufsort/VERSION.cmake b/loader/tools/dali/salvador/src/libdivsufsort/VERSION.cmake new file mode 100644 index 0000000..3f11ac1 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/VERSION.cmake @@ -0,0 +1,23 @@ +set(PROJECT_VERSION_MAJOR "2") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "2") +set(PROJECT_VERSION_EXTRA "-1") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(PROJECT_VERSION_FULL "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_EXTRA}") + +set(LIBRARY_VERSION "3.0.1") +set(LIBRARY_SOVERSION "3") + +## Git revision number ## +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + execute_process(COMMAND git describe --tags HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_DESCRIBE_TAGS ERROR_QUIET) + if(GIT_DESCRIBE_TAGS) + string(REGEX REPLACE "^v(.*)" "\\1" GIT_REVISION "${GIT_DESCRIBE_TAGS}") + string(STRIP "${GIT_REVISION}" GIT_REVISION) + if(GIT_REVISION) + set(PROJECT_VERSION_FULL "${GIT_REVISION}") + endif(GIT_REVISION) + endif(GIT_DESCRIBE_TAGS) +endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/CMakeLists.txt b/loader/tools/dali/salvador/src/libdivsufsort/examples/CMakeLists.txt new file mode 100644 index 0000000..e801c81 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +## Add definitions ## +add_definitions(-D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64) + +## Targets ## +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") +link_directories("${CMAKE_CURRENT_BINARY_DIR}/../lib") +foreach(src suftest mksary sasearch bwt unbwt) + add_executable(${src} ${src}.c) + target_link_libraries(${src} divsufsort) +endforeach(src) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/bwt.c b/loader/tools/dali/salvador/src/libdivsufsort/examples/bwt.c new file mode 100644 index 0000000..5a362d0 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/bwt.c @@ -0,0 +1,220 @@ +/* + * bwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +write_int(FILE *fp, saidx_t n) { + unsigned char c[4]; + c[0] = (unsigned char)((n >> 0) & 0xff), c[1] = (unsigned char)((n >> 8) & 0xff), + c[2] = (unsigned char)((n >> 16) & 0xff), c[3] = (unsigned char)((n >> 24) & 0xff); + return fwrite(c, sizeof(unsigned char), 4, fp); +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "bwt, a burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s [-b num] INFILE OUTFILE\n", progname); + fprintf(stderr, " -b num set block size to num MiB [1..512] (default: 32)\n\n"); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start,finish; + saint_t i, blocksize = 32, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if((argc != 3) && (argc != 5)) { print_help(argv[0], EXIT_FAILURE); } + i = 1; + if(argc == 5) { + if(strcmp(argv[i], "-b") != 0) { print_help(argv[0], EXIT_FAILURE); } + blocksize = atoi(argv[i + 1]); + if(blocksize < 0) { blocksize = 1; } + else if(512 < blocksize) { blocksize = 512; } + i += 2; + } + blocksize <<= 20; + + /* Open a file for reading. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[i], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[i], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + i += 1; + + /* Open a file for writing. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[i], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[i], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x20000000L < n) { n = 0x20000000L; } + if((blocksize == 0) || (n < blocksize)) { blocksize = (saidx_t)n; } + } else if(blocksize == 0) { blocksize = 32 << 20; } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + SA = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Write the blocksize. */ + if(write_int(ofp, blocksize) != 4) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + + fprintf(stderr, " BWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; 0 < (m = fread(T, sizeof(sauchar_t), blocksize, fp)); n += m) { + /* Burrows-Wheeler Transform. */ + pidx = divbwt(T, T, SA, m); + if(pidx < 0) { + fprintf(stderr, "%s (bw_transform): %s.\n", + argv[0], + (pidx == -1) ? "Invalid arguments" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write the bwted data. */ + if((write_int(ofp, pidx) != 4) || + (fwrite(T, sizeof(sauchar_t), m, ofp) != m)) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/mksary.c b/loader/tools/dali/salvador/src/libdivsufsort/examples/mksary.c new file mode 100644 index 0000000..b48177c --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/mksary.c @@ -0,0 +1,193 @@ +/* + * mksary.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "mksary, a simple suffix array builder, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Write the suffix array. */ + if(fwrite(SA, sizeof(saidx_t), (size_t)n, ofp) != (size_t)n) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/sasearch.c b/loader/tools/dali/salvador/src/libdivsufsort/examples/sasearch.c new file mode 100644 index 0000000..7e5ca4f --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/sasearch.c @@ -0,0 +1,165 @@ +/* + * sasearch.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "sasearch, a simple SA-based full-text search tool, version %s\n", + divsufsort_version()); + fprintf(stderr, "usage: %s PATTERN FILE SAFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *P; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t Psize; + saidx_t i, size, left; + + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 4) { print_help(argv[0], EXIT_FAILURE); } + + P = argv[1]; + Psize = strlen(P); + + /* Open a file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[2], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[2], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Open the SA file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[3], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[3], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Read n * sizeof(saidx_t) bytes of data. */ + if(fread(SA, sizeof(saidx_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Search and print */ + size = sa_search(T, (saidx_t)n, + (const sauchar_t *)P, (saidx_t)Psize, + SA, (saidx_t)n, &left); + for(i = 0; i < size; ++i) { + fprintf(stdout, "%" PRIdSAIDX_T "\n", SA[left + i]); + } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/suftest.c b/loader/tools/dali/salvador/src/libdivsufsort/examples/suftest.c new file mode 100644 index 0000000..71892ac --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/suftest.c @@ -0,0 +1,164 @@ +/* + * suftest.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "suftest, a suffixsort tester, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s FILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *fname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 1; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 2) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose = 0; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[1]); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Check the suffix array. */ + if(sufcheck(T, SA, (saidx_t)n, 1) != 0) { exit(EXIT_FAILURE); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/examples/unbwt.c b/loader/tools/dali/salvador/src/libdivsufsort/examples/unbwt.c new file mode 100644 index 0000000..c0f19e9 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/examples/unbwt.c @@ -0,0 +1,207 @@ +/* + * unbwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +read_int(FILE *fp, saidx_t *n) { + unsigned char c[4]; + size_t m = fread(c, sizeof(unsigned char), 4, fp); + if(m == 4) { + *n = (c[0] << 0) | (c[1] << 8) | + (c[2] << 16) | (c[3] << 24); + } + return m; +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "unbwt, an inverse burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *A; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start, finish; + saint_t err, blocksize, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Read the blocksize. */ + if(read_int(fp, &blocksize) != 4) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + A = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (A == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "UnBWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; (m = read_int(fp, &pidx)) != 0; n += m) { + /* Read blocksize bytes of data. */ + if((m != 4) || ((m = fread(T, sizeof(sauchar_t), blocksize, fp)) == 0)) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Inverse Burrows-Wheeler Transform. */ + if((err = inverse_bw_transform(T, T, A, m, pidx)) != 0) { + fprintf(stderr, "%s (reverseBWT): %s.\n", + argv[0], + (err == -1) ? "Invalid data" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write m bytes of data. */ + if(fwrite(T, sizeof(sauchar_t), m, ofp) != m) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(A); + free(T); + + return 0; +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/CMakeLists.txt b/loader/tools/dali/salvador/src/libdivsufsort/include/CMakeLists.txt new file mode 100644 index 0000000..37781cc --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/CMakeLists.txt @@ -0,0 +1,162 @@ +include(CheckIncludeFiles) +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) +include(CheckFunctionKeywords) +include(CheckLFS) + +## Checks for header files ## +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_include_file("memory.h" HAVE_MEMORY_H) +check_include_file("stddef.h" HAVE_STDDEF_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stdlib.h" HAVE_STDLIB_H) +check_include_file("string.h" HAVE_STRING_H) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) +if(HAVE_INTTYPES_H) + set(INCFILE "#include ") +elseif(HAVE_STDINT_H) + set(INCFILE "#include ") +else(HAVE_INTTYPES_H) + set(INCFILE "") +endif(HAVE_INTTYPES_H) + +## create configuration files from .cmake file ## +if(BUILD_EXAMPLES) + ## Checks for WinIO ## + if(WIN32) + check_include_file("io.h" HAVE_IO_H) + check_include_file("fcntl.h" HAVE_FCNTL_H) + check_symbol_exists("_setmode" "io.h;fcntl.h" HAVE__SETMODE) + if(NOT HAVE__SETMODE) + check_symbol_exists("setmode" "io.h;fcntl.h" HAVE_SETMODE) + endif(NOT HAVE__SETMODE) + check_symbol_exists("_fileno" "stdio.h" HAVE__FILENO) + check_symbol_exists("fopen_s" "stdio.h" HAVE_FOPEN_S) + check_symbol_exists("_O_BINARY" "fcntl.h" HAVE__O_BINARY) + endif(WIN32) + + ## Checks for large file support ## + check_lfs(WITH_LFS) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lfs.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lfs.h" @ONLY) +endif(BUILD_EXAMPLES) + +## generate config.h ## +check_function_keywords("inline;__inline;__inline__;__declspec(dllexport);__declspec(dllimport)") +if(HAVE_INLINE) + set(INLINE "inline") +elseif(HAVE___INLINE) + set(INLINE "__inline") +elseif(HAVE___INLINE__) + set(INLINE "__inline__") +else(HAVE_INLINE) + set(INLINE "") +endif(HAVE_INLINE) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +## Checks for types ## +# sauchar_t (8bit) +check_type_size("uint8_t" UINT8_T) +if(HAVE_UINT8_T) + set(SAUCHAR_TYPE "uint8_t") +else(HAVE_UINT8_T) + check_type_size("unsigned char" SIZEOF_UNSIGNED_CHAR) + if("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + set(SAUCHAR_TYPE "unsigned char") + else("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + message(FATAL_ERROR "Cannot find unsigned 8-bit integer type") + endif("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") +endif(HAVE_UINT8_T) +# saint_t (32bit) +check_type_size("int32_t" INT32_T) +if(HAVE_INT32_T) + set(SAINT32_TYPE "int32_t") + check_symbol_exists("PRId32" "inttypes.h" HAVE_PRID32) + if(HAVE_PRID32) + set(SAINT32_PRId "PRId32") + else(HAVE_PRID32) + set(SAINT32_PRId "\"d\"") + endif(HAVE_PRID32) +else(HAVE_INT32_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("short" SIZEOF_SHORT) + check_type_size("__int32" SIZEOF___INT32) + if("${SIZEOF_INT}" STREQUAL "4") + set(SAINT32_TYPE "int") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "4") + set(SAINT32_TYPE "long") + set(SAINT32_PRId "\"ld\"") + elseif("${SIZEOF_SHORT}" STREQUAL "4") + set(SAINT32_TYPE "short") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF___INT32}" STREQUAL "4") + set(SAINT32_TYPE "__int32") + set(SAINT32_PRId "\"d\"") + else("${SIZEOF_INT}" STREQUAL "4") + message(FATAL_ERROR "Cannot find 32-bit integer type") + endif("${SIZEOF_INT}" STREQUAL "4") +endif(HAVE_INT32_T) +# saint64_t (64bit) +if(BUILD_DIVSUFSORT64) + check_type_size("int64_t" INT64_T) + if(HAVE_INT64_T) + set(SAINT64_TYPE "int64_t") + check_symbol_exists("PRId64" "inttypes.h" HAVE_PRID64) + if(HAVE_PRID64) + set(SAINT64_PRId "PRId64") + else(HAVE_PRID64) + set(SAINT64_PRId "\"lld\"") + endif(HAVE_PRID64) + else(HAVE_INT64_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("long long" SIZEOF_LONG_LONG) + check_type_size("__int64" SIZEOF___INT64) + if("${SIZEOF_INT}" STREQUAL "8") + set(SAINT64_TYPE "int") + set(SAINT64_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long") + set(SAINT64_PRId "\"ld\"") + elseif("${SIZEOF_LONG_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long long") + set(SAINT64_PRId "\"lld\"") + elseif("${SIZEOF___INT64}" STREQUAL "8") + set(SAINT64_TYPE "__int64") + set(SAINT64_PRId "\"I64d\"") + else("${SIZEOF_INT}" STREQUAL "8") + message(SEND_ERROR "Cannot find 64-bit integer type") + set(BUILD_DIVSUFSORT64 OFF) + endif("${SIZEOF_INT}" STREQUAL "8") + endif(HAVE_INT64_T) +endif(BUILD_DIVSUFSORT64) + +## generate divsufsort.h ## +set(DIVSUFSORT_IMPORT "") +set(DIVSUFSORT_EXPORT "") +if(BUILD_SHARED_LIBS) + if(HAVE___DECLSPEC_DLLIMPORT_) + set(DIVSUFSORT_IMPORT "__declspec(dllimport)") + endif(HAVE___DECLSPEC_DLLIMPORT_) + if(HAVE___DECLSPEC_DLLEXPORT_) + set(DIVSUFSORT_EXPORT "__declspec(dllexport)") + endif(HAVE___DECLSPEC_DLLEXPORT_) +endif(BUILD_SHARED_LIBS) +set(W64BIT "") +set(SAINDEX_TYPE "${SAINT32_TYPE}") +set(SAINDEX_PRId "${SAINT32_PRId}") +set(SAINT_PRId "${SAINT32_PRId}") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + set(SAINDEX_TYPE "${SAINT64_TYPE}") + set(SAINDEX_PRId "${SAINT64_PRId}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/config.h.cmake b/loader/tools/dali/salvador/src/libdivsufsort/include/config.h.cmake new file mode 100644 index 0000000..6a1cf47 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/config.h.cmake @@ -0,0 +1,81 @@ +/* + * config.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CONFIG_H +#define _CONFIG_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Define to the version of this package. **/ +#cmakedefine PROJECT_VERSION_FULL "${PROJECT_VERSION_FULL}" + +/** Define to 1 if you have the header files. **/ +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_STDLIB_H 1 +#cmakedefine HAVE_STRING_H 1 +#cmakedefine HAVE_STRINGS_H 1 +#cmakedefine HAVE_MEMORY_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/** for WinIO **/ +#cmakedefine HAVE_IO_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE__SETMODE 1 +#cmakedefine HAVE_SETMODE 1 +#cmakedefine HAVE__FILENO 1 +#cmakedefine HAVE_FOPEN_S 1 +#cmakedefine HAVE__O_BINARY 1 +#ifndef HAVE__SETMODE +# if HAVE_SETMODE +# define _setmode setmode +# define HAVE__SETMODE 1 +# endif +# if HAVE__SETMODE && !HAVE__O_BINARY +# define _O_BINARY 0 +# define HAVE__O_BINARY 1 +# endif +#endif + +/** for inline **/ +#ifndef INLINE +# define INLINE @INLINE@ +#endif + +/** for VC++ warning **/ +#ifdef _MSC_VER +#pragma warning(disable: 4127) +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _CONFIG_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h new file mode 100644 index 0000000..7ebb412 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h @@ -0,0 +1,189 @@ +/* + * divsufsort.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DIVSUFSORT_API + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef unsigned char sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef int saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX_T +#define SAIDX_T +typedef int saidx_t; +#endif /* SAIDX_T */ +#ifndef PRIdSAIDX_T +#define PRIdSAIDX_T "d" +#endif + +/*- divsufsort context */ +typedef struct _divsufsort_ctx_t { + saidx_t *bucket_A; + saidx_t *bucket_B; +} divsufsort_ctx_t; + +/*- Prototypes -*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx); + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx); + +/** + * Constructs the suffix array of a given string. + * @param ctx suffix array context + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n); + +#if 0 +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *SA /* can NULL */, + saidx_t n, saidx_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *A /* can NULL */, + saidx_t n, saidx_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *left); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h.cmake b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h.cmake new file mode 100644 index 0000000..bcaba7c --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort.h.cmake @@ -0,0 +1,180 @@ +/* + * divsufsort@W64BIT@.h for libdivsufsort@W64BIT@ + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT@W64BIT@_H +#define _DIVSUFSORT@W64BIT@_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +@INCFILE@ + +#ifndef DIVSUFSORT_API +# ifdef DIVSUFSORT_BUILD_DLL +# define DIVSUFSORT_API @DIVSUFSORT_EXPORT@ +# else +# define DIVSUFSORT_API @DIVSUFSORT_IMPORT@ +# endif +#endif + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef @SAUCHAR_TYPE@ sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef @SAINT32_TYPE@ saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX@W64BIT@_T +#define SAIDX@W64BIT@_T +typedef @SAINDEX_TYPE@ saidx@W64BIT@_t; +#endif /* SAIDX@W64BIT@_T */ +#ifndef PRIdSAINT_T +#define PRIdSAINT_T @SAINT_PRId@ +#endif /* PRIdSAINT_T */ +#ifndef PRIdSAIDX@W64BIT@_T +#define PRIdSAIDX@W64BIT@_T @SAINDEX_PRId@ +#endif /* PRIdSAIDX@W64BIT@_T */ + + +/*- Prototypes -*/ + +/** + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +divsufsort@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t *SA, saidx@W64BIT@_t n); + +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +divbwt@W64BIT@(const sauchar_t *T, sauchar_t *U, saidx@W64BIT@_t *A, saidx@W64BIT@_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort@W64BIT@_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *SA /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *A /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck@W64BIT@(const sauchar_t *T, const saidx@W64BIT@_t *SA, saidx@W64BIT@_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_search@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const sauchar_t *P, saidx@W64BIT@_t Psize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saidx@W64BIT@_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_simplesearch@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saint_t c, saidx@W64BIT@_t *left); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT@W64BIT@_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_config.h b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_config.h new file mode 100644 index 0000000..4054a8a --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_config.h @@ -0,0 +1,9 @@ +#define HAVE_STRING_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDINT_H 1 +#define INLINE inline + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_private.h b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_private.h new file mode 100644 index 0000000..b4d97ad --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/divsufsort_private.h @@ -0,0 +1,205 @@ +/* + * divsufsort_private.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_PRIVATE_H +#define _DIVSUFSORT_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "divsufsort_config.h" +#include +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if defined(BUILD_DIVSUFSORT64) +# include "divsufsort64.h" +# ifndef SAIDX_T +# define SAIDX_T +# define saidx_t saidx64_t +# endif /* SAIDX_T */ +# ifndef PRIdSAIDX_T +# define PRIdSAIDX_T PRIdSAIDX64_T +# endif /* PRIdSAIDX_T */ +# define divsufsort divsufsort64 +# define divbwt divbwt64 +# define divsufsort_version divsufsort64_version +# define bw_transform bw_transform64 +# define inverse_bw_transform inverse_bw_transform64 +# define sufcheck sufcheck64 +# define sa_search sa_search64 +# define sa_simplesearch sa_simplesearch64 +# define sssort sssort64 +# define trsort trsort64 +#else +# include "divsufsort.h" +#endif + + +/*- Constants -*/ +#if !defined(UINT8_MAX) +# define UINT8_MAX (255) +#endif /* UINT8_MAX */ +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (UINT8_MAX + 1) +#endif +/* for divsufsort.c */ +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +/* for sssort.c */ +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) +# define SS_MISORT_STACKSIZE (96) +# else +# define SS_MISORT_STACKSIZE (64) +# endif +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#if defined(BUILD_DIVSUFSORT64) +# define SS_SMERGE_STACKSIZE (64) +#else +# define SS_SMERGE_STACKSIZE (32) +#endif +/* for trsort.c */ +#define TR_INSERTIONSORT_THRESHOLD (8) +#if defined(BUILD_DIVSUFSORT64) +# define TR_STACKSIZE (96) +#else +# define TR_STACKSIZE (64) +#endif + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +/* for divsufsort.c */ +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Prototypes -*/ +/* sssort.c */ +void +sssort(const sauchar_t *Td, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix); +/* trsort.c */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/include/lfs.h.cmake b/loader/tools/dali/salvador/src/libdivsufsort/include/lfs.h.cmake new file mode 100644 index 0000000..d5b84a8 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/include/lfs.h.cmake @@ -0,0 +1,56 @@ +/* + * lfs.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LFS_H +#define _LFS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __STRICT_ANSI__ +# define LFS_OFF_T @LFS_OFF_T@ +# define LFS_FOPEN @LFS_FOPEN@ +# define LFS_FTELL @LFS_FTELL@ +# define LFS_FSEEK @LFS_FSEEK@ +# define LFS_PRId @LFS_PRID@ +#else +# define LFS_OFF_T long +# define LFS_FOPEN fopen +# define LFS_FTELL ftell +# define LFS_FSEEK fseek +# define LFS_PRId "ld" +#endif +#ifndef PRIdOFF_T +# define PRIdOFF_T LFS_PRId +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _LFS_H */ diff --git a/loader/tools/dali/salvador/src/libdivsufsort/lib/CMakeLists.txt b/loader/tools/dali/salvador/src/libdivsufsort/lib/CMakeLists.txt new file mode 100644 index 0000000..abc90e6 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/lib/CMakeLists.txt @@ -0,0 +1,31 @@ +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") + +set(divsufsort_SRCS divsufsort.c sssort.c trsort.c utils.c) + +## libdivsufsort ## +add_library(divsufsort ${divsufsort_SRCS}) +install(TARGETS divsufsort + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +set_target_properties(divsufsort PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") + +## libdivsufsort64 ## +if(BUILD_DIVSUFSORT64) + add_library(divsufsort64 ${divsufsort_SRCS}) + install(TARGETS divsufsort64 + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties(divsufsort64 PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + COMPILE_FLAGS "-DBUILD_DIVSUFSORT64" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort.c b/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort.c new file mode 100644 index 0000000..50631ac --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort.c @@ -0,0 +1,431 @@ +/* + * divsufsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" +#ifdef _OPENMP +# include +#endif + + +/*- Private Functions -*/ + +/* Sorts suffixes of type B*. */ +static +saidx_t +sort_typeBstar(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n) { + saidx_t *PAb, *ISAb, *buf; +#ifdef _OPENMP + saidx_t *curbuf; + saidx_t l; +#endif + saidx_t i, j, k, t, m, bufsize; + saint_t c0, c1; +#ifdef _OPENMP + saint_t d0, d1; + int tmp; +#endif + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef _OPENMP + tmp = omp_get_max_threads(); + buf = SA + m, bufsize = (n - (2 * m)) / tmp; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) + { + tmp = omp_get_thread_num(); + curbuf = buf + tmp * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +#if 0 +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +saidx_t +construct_BWT(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k, *orig; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((saidx_t)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx) { + ctx->bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + ctx->bucket_B = NULL; + + if (ctx->bucket_A) { + ctx->bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + if (ctx->bucket_B) + return 0; + } + + divsufsort_destroy(ctx); + return -1; +} + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx) { + if (ctx->bucket_B) { + free(ctx->bucket_B); + ctx->bucket_B = NULL; + } + + if (ctx->bucket_A) { + free(ctx->bucket_A); + ctx->bucket_A = NULL; + } +} + +/*- Function -*/ + +saint_t +divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n) { + saidx_t m; + saint_t err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + /* Suffixsort. */ + if((ctx->bucket_A != NULL) && (ctx->bucket_B != NULL)) { + m = sort_typeBstar(T, SA, ctx->bucket_A, ctx->bucket_B, n); + construct_SA(T, SA, ctx->bucket_A, ctx->bucket_B, n, m); + } else { + err = -2; + } + + return err; +} + +#if 0 +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { + saidx_t *B; + saidx_t *bucket_A, *bucket_B; + saidx_t m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } + bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n); + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} + +const char * +divsufsort_version(void) { + return PROJECT_VERSION_FULL; +} +#endif diff --git a/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort_utils.c b/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort_utils.c new file mode 100644 index 0000000..f7cbc0d --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/lib/divsufsort_utils.c @@ -0,0 +1,383 @@ +/* + * utils.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Function -*/ + +#if 0 +/* Binary search for inverse bwt. */ +static +saidx_t +binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { + saidx_t half, i; + for(i = 0, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + if(A[i + half] < value) { + i += half + 1; + half -= (size & 1) ^ 1; + } + } + return i; +} + + +/*- Functions -*/ + +/* Burrows-Wheeler transform. */ +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, + saidx_t n, saidx_t *idx) { + saidx_t *A, i, j, p, t; + saint_t c; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } + if(n <= 1) { + if(n == 1) { U[0] = T[0]; } + *idx = n; + return 0; + } + + if((A = SA) == NULL) { + i = divbwt(T, U, NULL, n); + if(0 <= i) { *idx = i; i = 0; } + return (saint_t)i; + } + + /* BW transform. */ + if(T == U) { + t = n; + for(i = 0, j = 0; i < n; ++i) { + p = t - 1; + t = A[i]; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + j++; + } else { + *idx = i; + } + } + p = t - 1; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + } else { + *idx = i; + } + } else { + U[0] = T[n - 1]; + for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } + *idx = i + 1; + for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } + } + + if(SA == NULL) { + /* Deallocate memory. */ + free(A); + } + + return 0; +} + +/* Inverse Burrows-Wheeler transform. */ +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, + saidx_t n, saidx_t idx) { + saidx_t C[ALPHABET_SIZE]; + sauchar_t D[ALPHABET_SIZE]; + saidx_t *B; + saidx_t i, p; + saint_t c, d; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || + (n < idx) || ((0 < n) && (idx == 0))) { + return -1; + } + if(n <= 1) { return 0; } + + if((B = A) == NULL) { + /* Allocate n*sizeof(saidx_t) bytes of memory. */ + if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } + } + + /* Inverse BW transform. */ + for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { + p = C[c]; + if(0 < p) { + C[c] = i; + D[d++] = (sauchar_t)c; + i += p; + } + } + for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } + for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } + for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } + for(i = 0, p = idx; i < n; ++i) { + U[i] = D[binarysearch_lower(C, d, p)]; + p = B[p - 1]; + } + + if(A == NULL) { + /* Deallocate memory. */ + free(B); + } + + return 0; +} + +/* Checks the suffix array SA of the string T. */ +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, + saidx_t n, saint_t verbose) { + saidx_t C[ALPHABET_SIZE]; + saidx_t i, p, q, t; + saint_t c; + + if(verbose) { fprintf(stderr, "sufcheck: "); } + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { + if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } + return -1; + } + if(n == 0) { + if(verbose) { fprintf(stderr, "Done.\n"); } + return 0; + } + + /* check range: [0..n-1] */ + for(i = 0; i < n; ++i) { + if((SA[i] < 0) || (n <= SA[i])) { + if(verbose) { + fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + n - 1, i, SA[i]); + } + return -2; + } + } + + /* check first characters. */ + for(i = 1; i < n; ++i) { + if(T[SA[i - 1]] > T[SA[i]]) { + if(verbose) { + fprintf(stderr, "Suffixes in wrong order.\n" + " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" + " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", + i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); + } + return -3; + } + } + + /* check suffixes. */ + for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { + t = C[i]; + C[i] = p; + p += t; + } + + q = C[T[n - 1]]; + C[T[n - 1]] += 1; + for(i = 0; i < n; ++i) { + p = SA[i]; + if(0 < p) { + c = T[--p]; + t = C[c]; + } else { + c = T[p = n - 1]; + t = q; + } + if((t < 0) || (p != SA[t])) { + if(verbose) { + fprintf(stderr, "Suffix in wrong position.\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + t, (0 <= t) ? SA[t] : -1, i, SA[i]); + } + return -4; + } + if(t != q) { + ++C[c]; + if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } + } + } + + if(1 <= verbose) { fprintf(stderr, "Done.\n"); } + return 0; +} + + +static +int +_compare(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + saidx_t suf, saidx_t *match) { + saidx_t i, j; + saint_t r; + for(i = suf + *match, j = *match, r = 0; + (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } + *match = j; + return (r == 0) ? -(j != Psize) : r; +} + +/* Search for the pattern P in the string T. */ +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t match, lmatch, rmatch; + saidx_t llmatch, lrmatch, rlmatch, rrmatch; + saidx_t i, j, k; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (P == NULL) || (SA == NULL) || + (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } + + for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + match = MIN(lmatch, rmatch); + r = _compare(T, Tsize, P, Psize, SA[i + half], &match); + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + lmatch = match; + } else if(r > 0) { + rmatch = match; + } else { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + lmatch = MIN(llmatch, lrmatch); + r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + llmatch = lmatch; + } else { + lrmatch = lmatch; + } + } + + /* right part */ + for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + rmatch = MIN(rlmatch, rrmatch); + r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + rlmatch = rmatch; + } else { + rrmatch = rmatch; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} + +/* Search for the character c in the string T. */ +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t i, j, k, p; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + + for(i = j = k = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + p = SA[i + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + } else if(r == 0) { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + p = SA[j + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + } + } + + /* right part */ + for(half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + p = SA[k + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} +#endif diff --git a/loader/tools/dali/salvador/src/libdivsufsort/lib/sssort.c b/loader/tools/dali/salvador/src/libdivsufsort/lib/sssort.c new file mode 100644 index 0000000..4a18fd2 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/lib/sssort.c @@ -0,0 +1,815 @@ +/* + * sssort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +saint_t +ss_ilg(saidx_t n) { +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +# else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +# endif +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const saint_t sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +saidx_t +ss_isqrt(saidx_t x) { + saidx_t y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +saint_t +ss_compare(const sauchar_t *T, + const saidx_t *p1, const saidx_t *p2, + saidx_t depth) { + const sauchar_t *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *i, *j; + saidx_t t; + saint_t r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const sauchar_t *Td, const saidx_t *PA, + saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saint_t c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +ss_median3(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +ss_median5(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +saidx_t * +ss_partition(const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *a, *b; + saidx_t t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; + const sauchar_t *Td; + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t s, t; + saint_t ssize; + saint_t limit; + saint_t v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { + saidx_t t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { + saidx_t *a, *b, t; + saidx_t l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t depth) { + const saidx_t *p; + saidx_t *a, *b; + saidx_t len, half; + saint_t q, r; + saint_t x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + const saidx_t *p1, *p2; + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + saint_t x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t bufsize, saidx_t depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; + saidx_t *l, *r, *lm, *rm; + saidx_t m, len, half; + saint_t ssize; + saint_t check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Substring sort */ +void +sssort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix) { + saidx_t *a; +#if SS_BLOCKSIZE != 0 + saidx_t *b, *middle, *curbuf; + saidx_t j, k, curbufsize, limit; +#endif + saidx_t i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/lib/trsort.c b/loader/tools/dali/salvador/src/libdivsufsort/lib/trsort.c new file mode 100644 index 0000000..6fe3e67 --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/lib/trsort.c @@ -0,0 +1,586 @@ +/* + * trsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +static INLINE +saint_t +tr_ilg(saidx_t n) { +#if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +#else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *a, *b; + saidx_t t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saidx_t c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +tr_median5(const saidx_t *ISAd, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + saidx_t chance; + saidx_t remain; + saidx_t incval; + saidx_t count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +saint_t +trbudget_check(trbudget_t *budget, saidx_t size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const saidx_t *ISAd, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t **pa, saidx_t **pb, saidx_t v) { + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t t, s; + saidx_t x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + saidx_t *c, *d, *e; + saidx_t s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + saidx_t *c, *d, *e; + saidx_t s, v; + saidx_t rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(saidx_t *ISA, const saidx_t *ISAd, + saidx_t *SA, saidx_t *first, saidx_t *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; + saidx_t *a, *b, *c; + saidx_t t; + saidx_t v, x = 0; + saidx_t incr = ISAd - ISA; + saint_t limit, next; + saint_t ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Tandem repeat sort */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { + saidx_t *ISAd; + saidx_t *first, *last; + trbudget_t budget; + saidx_t t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} diff --git a/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/CMakeLists.txt b/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/CMakeLists.txt new file mode 100644 index 0000000..ee7063c --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/CMakeLists.txt @@ -0,0 +1,9 @@ +## generate libdivsufsort.pc ## +set(W64BIT "") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake b/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake new file mode 100644 index 0000000..6419d1e --- /dev/null +++ b/loader/tools/dali/salvador/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@@W64BIT@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION_FULL@ +URL: @PROJECT_URL@ +Libs: -L${libdir} -ldivsufsort@W64BIT@ +Cflags: -I${includedir} diff --git a/loader/tools/dali/salvador/src/libsalvador.h b/loader/tools/dali/salvador/src/libsalvador.h new file mode 100644 index 0000000..32ffffc --- /dev/null +++ b/loader/tools/dali/salvador/src/libsalvador.h @@ -0,0 +1,41 @@ +/* + * libsalvador.h - library definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _LIB_SALVADOR_H +#define _LIB_SALVADOR_H + +#include "format.h" +#include "shrink.h" +#include "expand.h" + +#define FLG_IS_INVERTED 1 /**< Use inverted (V2) format */ +#define FLG_IS_BACKWARD 2 /**< Use backward encoding */ + +#endif /* _LIB_SALVADOR_H */ diff --git a/loader/tools/dali/salvador/src/matchfinder.c b/loader/tools/dali/salvador/src/matchfinder.c new file mode 100644 index 0000000..e8641bf --- /dev/null +++ b/loader/tools/dali/salvador/src/matchfinder.c @@ -0,0 +1,393 @@ +/* + * matchfinder.c - LZ match finder implementation + * + * The following copying information applies to this specific source code file: + * + * Written in 2019-2021 by Emmanuel Marty + * Portions written in 2014-2015 by Eric Biggers + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide via the Creative Commons Zero 1.0 Universal Public Domain + * Dedication (the "CC0"). + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the CC0 for more details. + * + * You should have received a copy of the CC0 along with this software; if not + * see . + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include +#include +#include "matchfinder.h" +#include "format.h" +#include "libsalvador.h" + +/** + * Hash index into TAG_BITS + * + * @param nIndex index value + * + * @return hash + */ +static inline int salvador_get_index_tag(unsigned int nIndex) { + return (int)(((unsigned long long)nIndex * 11400714819323198485ULL) >> (64ULL - TAG_BITS)); +} + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int salvador_build_suffix_array(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize) { + unsigned long long *intervals = pCompressor->intervals; + + /* Build suffix array from input data */ + saidx_t *suffixArray = (saidx_t*)intervals; + if (divsufsort_build_array(&pCompressor->divsufsort_context, pInWindow, suffixArray, nInWindowSize) != 0) { + return 100; + } + + int i, r; + + for (i = nInWindowSize - 1; i >= 0; i--) { + intervals[i] = suffixArray[i]; + } + + int *PLCP = (int*)pCompressor->pos_data; /* Use temporarily */ + int *Phi = PLCP; + int nCurLen = 0; + + /* Compute the permuted LCP first (Kärkkäinen method) */ + Phi[intervals[0]] = -1; + for (i = 1; i < nInWindowSize; i++) + Phi[intervals[i]] = (unsigned int)intervals[i - 1]; + for (i = 0; i < nInWindowSize; i++) { + if (Phi[i] == -1) { + PLCP[i] = 0; + continue; + } + int nMaxLen = (i > Phi[i]) ? (nInWindowSize - i) : (nInWindowSize - Phi[i]); + while (nCurLen < nMaxLen && pInWindow[i + nCurLen] == pInWindow[Phi[i] + nCurLen]) nCurLen++; + PLCP[i] = nCurLen; + if (nCurLen > 0) + nCurLen--; + } + + /* Rotate permuted LCP into the LCP. This has better cache locality than the direct Kasai LCP method. This also + * saves us from having to build the inverse suffix array index, as the LCP is calculated without it using this method, + * and the interval builder below doesn't need it either. */ + intervals[0] &= POS_MASK; + + for (i = 1; i < nInWindowSize; i++) { + int nIndex = (int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < MIN_MATCH_SIZE) + nLen = 0; + if (nLen > LCP_MAX) + nLen = LCP_MAX; + int nTaggedLen = 0; + if (nLen) + nTaggedLen = (nLen << TAG_BITS) | (salvador_get_index_tag((unsigned int)nIndex) & ((1 << TAG_BITS) - 1)); + intervals[i] = ((unsigned long long)nIndex) | (((unsigned long long)nTaggedLen) << LCP_SHIFT); + } + + /** + * Build intervals for finding matches + * + * Methodology and code fragment taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + unsigned long long * const SA_and_LCP = intervals; + unsigned long long *pos_data = pCompressor->pos_data; + unsigned long long next_interval_idx; + unsigned long long *top = pCompressor->open_intervals; + unsigned long long prev_pos = SA_and_LCP[0] & POS_MASK; + + *top = 0; + intervals[0] = 0; + next_interval_idx = 1; + + for (r = 1; r < nInWindowSize; r++) { + const unsigned long long next_pos = SA_and_LCP[r] & POS_MASK; + const unsigned long long next_lcp = SA_and_LCP[r] & LCP_MASK; + const unsigned long long top_lcp = *top & LCP_MASK; + + if (next_lcp == top_lcp) { + /* Continuing the deepest open interval */ + pos_data[prev_pos] = *top; + } + else if (next_lcp > top_lcp) { + /* Opening a new interval */ + *++top = next_lcp | next_interval_idx++; + pos_data[prev_pos] = *top; + } + else { + /* Closing the deepest open interval */ + pos_data[prev_pos] = *top; + for (;;) { + const unsigned long long closed_interval_idx = *top-- & POS_MASK; + const unsigned long long superinterval_lcp = *top & LCP_MASK; + + if (next_lcp == superinterval_lcp) { + /* Continuing the superinterval */ + intervals[closed_interval_idx] = *top; + break; + } + else if (next_lcp > superinterval_lcp) { + /* Creating a new interval that is a + * superinterval of the one being + * closed, but still a subinterval of + * its superinterval */ + *++top = next_lcp | next_interval_idx++; + intervals[closed_interval_idx] = *top; + break; + } + else { + /* Also closing the superinterval */ + intervals[closed_interval_idx] = *top; + } + } + } + prev_pos = next_pos; + } + + /* Close any still-open intervals. */ + pos_data[prev_pos] = *top; + for (; top > pCompressor->open_intervals; top--) + intervals[*top & POS_MASK] = *(top - 1); + + /* Success */ + return 0; +} + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param pMatchDepth pointer to returned match depths + * @param nMaxMatches maximum number of matches to return (0 for none) + * + * @return number of matches + */ +static int salvador_find_matches_at(salvador_compressor *pCompressor, const int nOffset, salvador_match *pMatches, unsigned short *pMatchDepth, const int nMaxMatches) { + unsigned long long *intervals = pCompressor->intervals; + unsigned long long *pos_data = pCompressor->pos_data; + unsigned long long ref; + unsigned long long super_ref; + unsigned long long match_pos; + salvador_match *matchptr; + unsigned short *depthptr; + int nPrevOffset, nPrevLen, nCurDepth; + unsigned short* cur_depth; + int nMatchOffset, nMatchLen; + const int nMaxOffset = pCompressor->max_offset; + + /** + * Find matches using intervals + * + * Taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + + /* Get the deepest lcp-interval containing the current suffix. */ + ref = pos_data[nOffset]; + + pos_data[nOffset] = 0; + + /* Ascend until we reach a visited interval, the root, or a child of the + * root. Link unvisited intervals to the current suffix as we go. */ + while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) { + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + ref = super_ref; + } + + if (super_ref == 0) { + /* In this case, the current interval may be any of: + * (1) the root; + * (2) an unvisited child of the root */ + + if (ref != 0) /* Not the root? */ + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + return 0; + } + + /* Ascend indirectly via pos_data[] links. */ + + match_pos = super_ref & EXCL_VISITED_MASK; + matchptr = pMatches; + depthptr = pMatchDepth; + + nPrevOffset = 0; + nPrevLen = 0; + nCurDepth = 0; + cur_depth = NULL; + + if ((matchptr - pMatches) < nMaxMatches) { + nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset) { + nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS)); + + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + + for (;;) { + if ((super_ref = pos_data[match_pos]) > ref) { + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + if ((matchptr - pMatches) < nMaxMatches) { + nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset && nMatchOffset != nPrevOffset) { + nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + } + + while ((super_ref = pos_data[match_pos]) > ref) + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + pos_data[match_pos] = (unsigned long long)ref; + + if ((matchptr - pMatches) < nMaxMatches) { + nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset && nMatchOffset != nPrevOffset) { + nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + + if (super_ref == 0) + break; + ref = super_ref; + match_pos = intervals[ref & POS_MASK] & EXCL_VISITED_MASK; + + if ((matchptr - pMatches) < nMaxMatches) { + nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset && nMatchOffset != nPrevOffset) { + nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + } + + return (int)(matchptr - pMatches); +} + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void salvador_skip_matches(salvador_compressor *pCompressor, const int nStartOffset, const int nEndOffset) { + salvador_match match; + unsigned short depth; + int i; + + /* Skipping still requires scanning for matches, as this also performs a lazy update of the intervals. However, + * we don't store the matches. */ + for (i = nStartOffset; i < nEndOffset; i++) { + salvador_find_matches_at(pCompressor, i, &match, &depth, 0); + } +} + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + */ +void salvador_find_all_matches(salvador_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset, const int nBlockFlags) { + salvador_match *pMatch = pCompressor->match; + unsigned short *pMatchDepth = pCompressor->match_depth; + int i; + + for (i = nStartOffset; i < nEndOffset; i++) { + int nMatches = salvador_find_matches_at(pCompressor, i, pMatch, pMatchDepth, nMatchesPerOffset); + + if (nMatches < nMatchesPerOffset) { + memset(pMatch + nMatches, 0, (nMatchesPerOffset - nMatches) * sizeof(salvador_match)); + memset(pMatchDepth + nMatches, 0, (nMatchesPerOffset - nMatches) * sizeof(unsigned short)); + } + + pMatch += nMatchesPerOffset; + pMatchDepth += nMatchesPerOffset; + } +} diff --git a/loader/tools/dali/salvador/src/matchfinder.h b/loader/tools/dali/salvador/src/matchfinder.h new file mode 100644 index 0000000..783c92b --- /dev/null +++ b/loader/tools/dali/salvador/src/matchfinder.h @@ -0,0 +1,77 @@ +/* + * matchfinder.h - LZ match finder definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _MATCHFINDER_H +#define _MATCHFINDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +typedef struct _salvador_match salvador_match; +typedef struct _salvador_compressor salvador_compressor; + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int salvador_build_suffix_array(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize); + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void salvador_skip_matches(salvador_compressor *pCompressor, const int nStartOffset, const int nEndOffset); + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + */ +void salvador_find_all_matches(salvador_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset, const int nBlockFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATCHFINDER_H */ diff --git a/loader/tools/dali/salvador/src/salvador.c b/loader/tools/dali/salvador/src/salvador.c new file mode 100644 index 0000000..db3abff --- /dev/null +++ b/loader/tools/dali/salvador/src/salvador.c @@ -0,0 +1,1248 @@ +/* + * salvador.c - command line compression utility for the salvador library + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif +#include "libsalvador.h" + +#define OPT_VERBOSE 1 +#define OPT_STATS 2 +#define OPT_BACKWARD 4 +#define OPT_CLASSIC 8 + +#define TOOL_VERSION "1.3.0" + +/*---------------------------------------------------------------------------*/ + +#ifdef _WIN32 +LARGE_INTEGER hpc_frequency; +BOOL hpc_available = FALSE; +#endif + +static void do_init_time() { +#ifdef _WIN32 + hpc_frequency.QuadPart = 0; + hpc_available = QueryPerformanceFrequency(&hpc_frequency); +#endif +} + +static long long do_get_time() { + long long nTime; + +#ifdef _WIN32 + if (hpc_available) { + LARGE_INTEGER nCurTime; + + /* Use HPC hardware for best precision */ + QueryPerformanceCounter(&nCurTime); + nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart); + } + else { + struct _timeb tb; + _ftime(&tb); + + nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL; + } +#else + struct timeval tm; + gettimeofday(&tm, NULL); + + nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec; +#endif + return nTime; +} + +static void do_reverse_buffer(unsigned char *pBuffer, size_t nBufferSize) { + size_t nMidPoint = nBufferSize / 2; + size_t i, j; + + for (i = 0, j = nBufferSize - 1; i < nMidPoint; i++, j--) { + unsigned char c = pBuffer[i]; + pBuffer[i] = pBuffer[j]; + pBuffer[j] = c; + } +} + +/*---------------------------------------------------------------------------*/ + +static void compression_progress(long long nOriginalSize, long long nCompressedSize) { + if (nOriginalSize >= 512 * 1024) { + fprintf(stdout, "\r%lld => %lld (%g %%) \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + fflush(stdout); + } +} + +static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nOriginalSize = 0L, nCompressedSize = 0L, nMaxCompressedSize; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + salvador_stats stats; + unsigned char *pDecompressedData; + unsigned char *pCompressedData; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + if (f_dict) fclose(f_dict); + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nOriginalSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nOriginalSize); + if (!pDecompressedData) { + fclose(f_in); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nOriginalSize); + return 100; + } + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? nOriginalSize : 0), 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + fclose(f_in); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + } + + /* Read input file data */ + if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? 0 : nDictionarySize), 1, nOriginalSize, f_in) != nOriginalSize) { + free(pDecompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize + nOriginalSize); + + /* Allocate max compressed size */ + + nMaxCompressedSize = salvador_get_max_compressed_size(nDictionarySize + nOriginalSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize); + if (!pCompressedData) { + free(pDecompressedData); + fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData, 0, nMaxCompressedSize); + + nCompressedSize = salvador_compress(pDecompressedData, pCompressedData, nDictionarySize + nOriginalSize, nMaxCompressedSize, nFlags, nMaxWindowSize, nDictionarySize, compression_progress, &stats); + + if ((nOptions & OPT_VERBOSE)) { + nEndTime = do_get_time(); + } + + if (nCompressedSize == -1) { + free(pCompressedData); + free(pDecompressedData); + fprintf(stderr, "compression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole compressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pCompressedData, 1, nCompressedSize, f_out); + fclose(f_out); + } + } + + free(pCompressedData); + free(pDecompressedData); + + if ((nOptions & OPT_VERBOSE)) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %d into %d bytes ==> %g %%\n", + pszInFilename, fDelta, fSpeed, stats.commands_divisor, (double)nOriginalSize / (double)stats.commands_divisor, + (int)nOriginalSize, (int)nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + } + + if (nOptions & OPT_STATS) { + if (stats.literals_divisor > 0) + fprintf(stdout, "Literals: min: %d avg: %d max: %d count: %d\n", stats.min_literals, stats.total_literals / stats.literals_divisor, stats.max_literals, stats.literals_divisor); + else + fprintf(stdout, "Literals: none\n"); + + fprintf(stdout, "Normal matches: %d rep matches: %d EOD: %d\n", + stats.num_normal_matches, stats.num_rep_matches, stats.num_eod); + + if (stats.match_divisor > 0) { + fprintf(stdout, "Offsets: min: %d avg: %d max: %d count: %d\n", stats.min_offset, (int)(stats.total_offsets / (long long)stats.match_divisor), stats.max_offset, stats.match_divisor); + fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor); + } + else { + fprintf(stdout, "Offsets: none\n"); + fprintf(stdout, "Match lens: none\n"); + } + if (stats.rle1_divisor > 0) { + fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor); + } + else { + fprintf(stdout, "RLE1 lens: none\n"); + } + if (stats.rle2_divisor > 0) { + fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor); + } + else { + fprintf(stdout, "RLE2 lens: none\n"); + } + fprintf(stdout, "Safe distance: %d (0x%X)\n", stats.safe_dist, stats.safe_dist); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize; + unsigned char *pCompressedData; + unsigned char *pDecompressedData; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nCompressedSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pCompressedData = (unsigned char*)malloc(nCompressedSize); + if (!pCompressedData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nCompressedSize); + return 100; + } + + if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) { + free(pCompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + /* Get max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags); + if (nMaxDecompressedSize == -1) { + free(pCompressedData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Allocate max decompressed size */ + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize); + if (!pDecompressedData) { + free(pCompressedData); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize); + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + fclose(f_in); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize); + } + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nOriginalSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags); + if (nOriginalSize == -1) { + free(pDecompressedData); + free(pCompressedData); + + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData + nDictionarySize, nOriginalSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole decompressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pDecompressedData + nDictionarySize, 1, nOriginalSize, f_out); + fclose(f_out); + } + } + + free(pDecompressedData); + free(pCompressedData); + + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize, nDecompressedSize; + unsigned char *pCompressedData = NULL; + unsigned char *pOriginalData = NULL; + unsigned char *pDecompressedData = NULL; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nCompressedSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pCompressedData = (unsigned char*)malloc(nCompressedSize); + if (!pCompressedData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nCompressedSize); + return 100; + } + + if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) { + free(pCompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + /* Read the whole original file in memory */ + + f_in = fopen(pszOutFilename, "rb"); + if (!f_in) { + free(pCompressedData); + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nOriginalSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pOriginalData = (unsigned char*)malloc(nOriginalSize); + if (!pOriginalData) { + fclose(f_in); + free(pCompressedData); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nOriginalSize); + return 100; + } + + if (fread(pOriginalData, 1, nOriginalSize, f_in) != nOriginalSize) { + free(pOriginalData); + fclose(f_in); + free(pCompressedData); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + /* Get max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags); + if (nMaxDecompressedSize == -1) { + free(pOriginalData); + free(pCompressedData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Allocate max decompressed size */ + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize); + if (!pDecompressedData) { + free(pOriginalData); + free(pCompressedData); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize); + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + fclose(f_in); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize); + } + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nDecompressedSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags); + if (nDecompressedSize == -1) { + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData + nDictionarySize, nDecompressedSize); + + if (nDecompressedSize != nOriginalSize || memcmp(pDecompressedData + nDictionarySize, pOriginalData, nOriginalSize)) { + fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename); + return 100; + } + + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) { + size_t nIndex = 0; + int nMatchProbability = (int)(fMatchProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + pBuffer[nIndex++] = rand() % nNumLiteralValues; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) >= nMatchProbability) { + size_t nLiteralCount = rand() & 127; + if (nLiteralCount > (nBufferSize - nIndex)) + nLiteralCount = nBufferSize - nIndex; + + while (nLiteralCount--) + pBuffer[nIndex++] = rand() % nNumLiteralValues; + } + else { + size_t nMatchLength = MIN_MATCH_SIZE + (rand() & 1023); + size_t nMatchOffset; + + if (nMatchLength > (nBufferSize - nIndex)) + nMatchLength = nBufferSize - nIndex; + if (nMatchLength > nIndex) + nMatchLength = nIndex; + + if (nMatchLength < nIndex) + nMatchOffset = rand() % (nIndex - nMatchLength); + else + nMatchOffset = 0; + + while (nMatchLength--) { + pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset]; + nIndex++; + } + } + } +} + +static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) { + size_t nIndex = 0; + int nXorProbability = (int)(fXorProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) < nXorProbability) { + pBuffer[nIndex] ^= 0xff; + } + nIndex++; + } +} + +static int do_self_test(const unsigned int nOptions, const unsigned int nMaxWindowSize, const int nIsQuickTest) { + unsigned char *pGeneratedData; + unsigned char *pCompressedData; + unsigned char *pTmpCompressedData; + unsigned char *pTmpDecompressedData; + size_t nGeneratedDataSize; + size_t nMaxCompressedDataSize; + unsigned int nSeed = 123; + int nFlags = FLG_IS_INVERTED; + int i; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pGeneratedData) { + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + nMaxCompressedDataSize = salvador_get_max_compressed_size(4 * BLOCK_SIZE); + pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pCompressedData) { + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pTmpCompressedData) { + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pTmpDecompressedData) { + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + memset(pGeneratedData, 0, 4 * BLOCK_SIZE); + memset(pCompressedData, 0, nMaxCompressedDataSize); + memset(pTmpCompressedData, 0, nMaxCompressedDataSize); + + /* Test compressing with a too small buffer to do anything, expect to fail cleanly */ + for (i = 0; i < 12; i++) { + generate_compressible_data(pGeneratedData, i, nSeed, 256, 0.5f); + salvador_compress(pGeneratedData, pCompressedData, i, i, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + } + + size_t nDataSizeStep = 128; + float fProbabilitySizeStep = nIsQuickTest ? 0.005f : 0.0005f; + + for (nGeneratedDataSize = 1024; nGeneratedDataSize <= (nIsQuickTest ? 1024U : (4U * BLOCK_SIZE)); nGeneratedDataSize += nDataSizeStep) { + float fMatchProbability; + + fprintf(stdout, "size %zd", nGeneratedDataSize); + for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) { + int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 }; + float fXorProbability; + + fputc('.', stdout); + fflush(stdout); + + for (i = 0; i < 12; i++) { + /* Generate data to compress */ + generate_compressible_data(pGeneratedData, nGeneratedDataSize, nSeed, nNumLiteralValues[i], fMatchProbability); + + /* Try to compress it, expected to succeed */ + size_t nActualCompressedSize = salvador_compress(pGeneratedData, pCompressedData, nGeneratedDataSize, salvador_get_max_compressed_size(nGeneratedDataSize), + nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + if (nActualCompressedSize == -1 || nActualCompressedSize < (1 + 1 + 1 /* footer */)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error compressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress it, expected to succeed */ + size_t nActualDecompressedSize; + nActualDecompressedSize = salvador_decompress(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags); + if (nActualDecompressedSize == -1) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error decompressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */ + for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) { + memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize); + xor_data(pTmpCompressedData, nActualCompressedSize, nSeed, fXorProbability); + salvador_decompress(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags); + } + } + + nSeed++; + } + + fputc(10, stdout); + fflush(stdout); + + nDataSizeStep <<= 1; + if (nDataSizeStep > (128 * 4096)) + nDataSizeStep = 128 * 4096; + fProbabilitySizeStep *= 1.25; + if (fProbabilitySizeStep > (0.0005f * 4096)) + fProbabilitySizeStep = 0.0005f * 4096; + } + + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + + free(pTmpCompressedData); + pTmpCompressedData = NULL; + + free(pCompressedData); + pCompressedData = NULL; + + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stdout, "All tests passed.\n"); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) { + size_t nFileSize, nMaxCompressedSize; + unsigned char *pFileData; + unsigned char *pCompressedData; + int nFlags = FLG_IS_INVERTED; + int i; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pFileData, nFileSize); + + /* Allocate max compressed size */ + + nMaxCompressedSize = salvador_get_max_compressed_size(nFileSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048); + if (!pCompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData + 1024, 0, nMaxCompressedSize); + + long long nBestCompTime = -1; + + size_t nActualCompressedSize = 0; + size_t nRightGuardPos = nMaxCompressedSize; + + for (i = 0; i < 5; i++) { + unsigned char nGuard = 0x33 + i; + int j; + + /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */ + memset(pCompressedData, nGuard, 1024); + memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024); + + long long t0 = do_get_time(); + nActualCompressedSize = salvador_compress(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + long long t1 = do_get_time(); + if (nActualCompressedSize == -1) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "compression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestCompTime == -1 || nBestCompTime > nCurDecTime) + nBestCompTime = nCurDecTime; + + /* Check guard bytes before the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024); + return 100; + } + } + + /* Check guard bytes after the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j); + return 100; + } + } + + nRightGuardPos = nActualCompressedSize; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData + 1024, nActualCompressedSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole compressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out); + fclose(f_out); + } + } + + free(pCompressedData); + free(pFileData); + + fprintf(stdout, "compressed size: %zd bytes\n", nActualCompressedSize); + fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + size_t nFileSize, nMaxDecompressedSize; + unsigned char *pFileData; + unsigned char *pDecompressedData; + int nFlags = FLG_IS_INVERTED; + int i; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pFileData, nFileSize); + + /* Allocate max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pFileData, nFileSize, nFlags); + if (nMaxDecompressedSize == -1) { + free(pFileData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize); + if (!pDecompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nMaxDecompressedSize); + + long long nBestDecTime = -1; + + size_t nActualDecompressedSize = 0; + for (i = 0; i < 50; i++) { + long long t0 = do_get_time(); + nActualDecompressedSize = salvador_decompress(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, 0 /* dictionary size */, nFlags); + long long t1 = do_get_time(); + if (nActualDecompressedSize == -1) { + free(pDecompressedData); + free(pFileData); + fprintf(stderr, "decompression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestDecTime == -1 || nBestDecTime > nCurDecTime) + nBestDecTime = nCurDecTime; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nActualDecompressedSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole decompressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out); + fclose(f_out); + } + } + + free(pDecompressedData); + free(pFileData); + + fprintf(stdout, "decompressed size: %zd bytes\n", nActualDecompressedSize); + fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +int main(int argc, char **argv) { + int i; + const char *pszInFilename = NULL; + const char *pszOutFilename = NULL; + const char *pszDictionaryFilename = NULL; + int nArgsError = 0; + int nCommandDefined = 0; + int nVerifyCompression = 0; + char cCommand = 'z'; + unsigned int nOptions = 0; + unsigned int nMaxWindowSize = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'd'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-z")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'z'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-c")) { + if (!nVerifyCompression) { + nVerifyCompression = 1; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-cbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'B'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-dbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'b'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-test")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 't'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-quicktest")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'T'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-D")) { + if (!pszDictionaryFilename && (i + 1) < argc) { + pszDictionaryFilename = argv[i + 1]; + i++; + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-D", 2)) { + if (!pszDictionaryFilename) { + pszDictionaryFilename = argv[i] + 2; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-v")) { + if ((nOptions & OPT_VERBOSE) == 0) { + nOptions |= OPT_VERBOSE; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-w")) { + if (!nMaxWindowSize && (i + 1) < argc) { + char *pEnd = NULL; + nMaxWindowSize = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET)) { + i++; + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-w", 2)) { + if (!nMaxWindowSize) { + char *pEnd = NULL; + nMaxWindowSize = (int)strtol(argv[i] + 2, &pEnd, 10); + if (!(pEnd && pEnd != (argv[i] + 2) && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET))) { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-stats")) { + if ((nOptions & OPT_STATS) == 0) { + nOptions |= OPT_STATS; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-b")) { + if ((nOptions & OPT_BACKWARD) == 0) { + nOptions |= OPT_BACKWARD; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-classic")) { + if ((nOptions & OPT_CLASSIC) == 0) { + nOptions |= OPT_CLASSIC; + } + else + nArgsError = 1; + } + else { + if (!pszInFilename) + pszInFilename = argv[i]; + else { + if (!pszOutFilename) + pszOutFilename = argv[i]; + else + nArgsError = 1; + } + } + } + + if (!nArgsError && cCommand == 't') { + return do_self_test(nOptions, nMaxWindowSize, 0); + } + else if (!nArgsError && cCommand == 'T') { + return do_self_test(nOptions, nMaxWindowSize, 1); + } + + if (nArgsError || !pszInFilename || !pszOutFilename) { + fprintf(stderr, "salvador command-line tool v" TOOL_VERSION " by Emmanuel Marty\n"); + fprintf(stderr, "usage: %s [-c] [-d] [-v] [-b] \n", argv[0]); + fprintf(stderr, " -c: check resulting stream after compressing\n"); + fprintf(stderr, " -d: decompress (default: compress)\n"); + fprintf(stderr, " -b: backwards compression or decompression\n"); + fprintf(stderr, " -w : maximum window size, in bytes (16..32639), defaults to maximum\n"); + fprintf(stderr, " -D : use dictionary file\n"); + fprintf(stderr, " -cbench: benchmark in-memory compression\n"); + fprintf(stderr, " -dbench: benchmark in-memory decompression\n"); + fprintf(stderr, " -test: run full automated self-tests\n"); + fprintf(stderr, "-quicktest: run quick automated self-tests\n"); + fprintf(stderr, " -stats: show compressed data stats\n"); + fprintf(stderr, " -classic: encode and decode using classical (V1) format, defaults to modern (V2)\n"); + fprintf(stderr, " -v: be verbose\n"); + return 100; + } + + do_init_time(); + + if (cCommand == 'z') { + int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize); + if (nResult == 0 && nVerifyCompression) { + return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions); + } else { + return nResult; + } + } + else if (cCommand == 'd') { + return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions); + } + else if (cCommand == 'B') { + return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize); + } + else if (cCommand == 'b') { + return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions); + } + else { + return 100; + } +} diff --git a/loader/tools/dali/salvador/src/shrink.c b/loader/tools/dali/salvador/src/shrink.c new file mode 100644 index 0000000..4ef4469 --- /dev/null +++ b/loader/tools/dali/salvador/src/shrink.c @@ -0,0 +1,2041 @@ +/* + * shrink.c - compressor implementation + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include +#include +#include +#include "libsalvador.h" +#include "matchfinder.h" +#include "shrink.h" +#include "format.h" + +#define MIN_ENCODED_MATCH_SIZE 2 +#define TOKEN_SIZE 1 +#define OFFSET_COST(__offset) (((__offset) <= 128) ? 8 : (7 + salvador_get_elias_size((((__offset) - 1) >> 7) + 1))) + +/** Costs, per length */ +static const char salvador_cost_for_len[8192] = { + 0, 2, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +}; + +/** + * Get the number of bits required to encode a gamma value + * + * @param nValue value to encode as gamma + * + * @return number of bits required for encoding + */ +static int salvador_get_elias_size(const int nValue) { + if (nValue >= 0 && nValue < 8192) { + return salvador_cost_for_len[nValue] - TOKEN_SIZE; + } + else { + int i = nValue; + int nBits = 0; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + while ((i >>= 1) > 0) { + nBits++; + nBits++; + } + + nBits++; + + return nBits; + } +} + +/** + * Write packed 0 control bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_zero_ctrl_bit(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift) { + if (nOutOffset >= 0) { + if ((*nCurBitShift) == -1) { + /* Allocate a new byte in the stream to pack bits in */ + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurBitsOffset) = nOutOffset; + (*nCurBitShift) = 7; + pOutData[nOutOffset++] = 0; + } + + (*nCurBitShift)--; + } + + return nOutOffset; +} + +/** + * Write packed 1 control bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_one_ctrl_bit(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, int* nCurBitsOffset, int* nCurBitShift) { + if (nOutOffset >= 0) { + if ((*nCurBitShift) == -1) { + /* Allocate a new byte in the stream to pack bits in */ + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurBitsOffset) = nOutOffset; + (*nCurBitShift) = 7; + pOutData[nOutOffset++] = 0; + } + + pOutData[(*nCurBitsOffset)] |= 1 << ((*nCurBitShift)--); + } + + return nOutOffset; +} + +/** + * Write packed data bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue bit value to write + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_data_bit(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, int* nCurBitsOffset, int* nCurBitShift) { + if (nOutOffset >= 0) { + pOutData[(*nCurBitsOffset)] |= nValue << ((*nCurBitShift)--); + } + + return nOutOffset; +} + +/** + * Write normally encoded, interlaced elias gamma value to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_normal_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Write inverted, interlaced elias gamma value to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_inverted_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 0 : 1, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 0 : 1, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Write elias gamma encoded value to output (compressed) buffer, with the first bit stored in a different (match offset) byte + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_split_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + i >>= 1; + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int salvador_get_literals_varlen_size(const int nLength) { + if (nLength >= 0 && nLength < 8192) + return salvador_cost_for_len[nLength]; + else + return TOKEN_SIZE + salvador_get_elias_size(nLength); +} + +/** + * Get the number of extra bits required to represent a non-rep match length + * + * @param __nLength encoded match length (actual match length - MIN_ENCODED_MATCH_SIZE) + * + * @return number of extra bits required + */ +#define salvador_get_match_varlen_size_norep(__nLength) salvador_get_elias_size((__nLength) + 1) + +/** + * Get the number of extra bits required to represent a repmatch length + * + * @param __nLength encoded match length (actual match length - MIN_ENCODED_MATCH_SIZE) + * + * @return number of extra bits required + */ +#define salvador_get_match_varlen_size_rep(__nLength) salvador_get_elias_size((__nLength) + 1 + 1) + +/** + * Insert forward rep candidate + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param i input data window position whose matches are being considered + * @param nMatchOffset match offset to use as rep candidate + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nDepth current insertion depth + */ +static void salvador_insert_forward_match(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, const int nDepth) { + const salvador_arrival *arrival = pCompressor->arrival + ((i - nStartOffset) * pCompressor->max_arrivals_per_position); + const int *rle_len = (const int*)pCompressor->intervals /* reuse */; + salvador_visited* visited = ((salvador_visited*)pCompressor->pos_data) - nStartOffset /* reuse */; + int j; + + for (j = 0; j < NINITIAL_ARRIVALS_PER_POSITION && arrival[j].from_slot; j++) { + if (arrival[j].num_literals) { + const int nRepOffset = arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset) { + const int nRepPos = arrival[j].rep_pos; + + if (nRepPos >= nStartOffset && + nRepPos < nEndOffset && + visited[nRepPos] != nMatchOffset) { + + visited[nRepPos] = nMatchOffset; + + if (nRepPos >= nMatchOffset) { + salvador_match* fwd_match = pCompressor->match + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT); + + if (fwd_match[NMATCHES_PER_INDEX - 1].length == 0) { + const unsigned char* pInWindowStart = pInWindow + nRepPos; + + if (pInWindowStart[0] == pInWindowStart[-nMatchOffset]) { + if (nRepOffset) { + const int nLen0 = rle_len[nRepPos - nMatchOffset]; + const int nLen1 = rle_len[nRepPos]; + const int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + + int nMaxRepLen = nEndOffset - nRepPos; + if (nMaxRepLen > LCP_MAX) + nMaxRepLen = LCP_MAX; + + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLen; + const unsigned char* pInWindowAtRepOffset = pInWindowStart + nMinLen; + + if (pInWindowAtRepOffset > pInWindowMax) + pInWindowAtRepOffset = pInWindowMax; + + while ((pInWindowAtRepOffset + 8) < pInWindowMax && !memcmp(pInWindowAtRepOffset, pInWindowAtRepOffset - nMatchOffset, 8)) + pInWindowAtRepOffset += 8; + while ((pInWindowAtRepOffset + 4) < pInWindowMax && !memcmp(pInWindowAtRepOffset, pInWindowAtRepOffset - nMatchOffset, 4)) + pInWindowAtRepOffset += 4; + while (pInWindowAtRepOffset < pInWindowMax && pInWindowAtRepOffset[0] == pInWindowAtRepOffset[-nMatchOffset]) + pInWindowAtRepOffset++; + + const int nCurRepLen = (const int)(pInWindowAtRepOffset - pInWindowStart); + + unsigned short* fwd_depth = pCompressor->match_depth + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT); + int r; + + for (r = 0; fwd_match[r].length; r++) { + if (fwd_match[r].offset == nMatchOffset) { + if ((int)fwd_match[r].length < nCurRepLen && fwd_depth[r] == 0) { + fwd_match[r].length = nCurRepLen; + fwd_depth[r] = 0; + } + break; + } + } + + if (!fwd_match[r].length) { + fwd_match[r].offset = nMatchOffset; + fwd_match[r].length = nCurRepLen; + fwd_depth[r] = 0; + + if (nDepth < 9) + salvador_insert_forward_match(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nDepth + 1); + } + } + } + } + } + } + } + } + } +} + +/** + * Attempt to pick optimal matches, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates + * @param nCurRepMatchOffset starting rep offset for this block + * @param nArrivalsPerPosition number of arrivals to record per input buffer position + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + */ +static void salvador_optimize_forward(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, const int nInsertForwardReps, const int *nCurRepMatchOffset, const int nArrivalsPerPosition, const int nBlockFlags) { + const int nMaxArrivalsPerPosition = pCompressor->max_arrivals_per_position; + salvador_arrival *arrival = pCompressor->arrival - (nStartOffset * nMaxArrivalsPerPosition); + const int* rle_len = (const int*)pCompressor->intervals /* reuse */; + salvador_visited* visited = ((salvador_visited*)pCompressor->pos_data) - nStartOffset /* reuse */; + int i; + + if ((nEndOffset - nStartOffset) > pCompressor->block_size) return; + + for (i = (nStartOffset * nMaxArrivalsPerPosition); i != ((nEndOffset+1) * nMaxArrivalsPerPosition); i += nMaxArrivalsPerPosition) { + int j; + + memset(arrival + i, 0, sizeof(salvador_arrival) * nMaxArrivalsPerPosition); + + for (j = 0; j < nMaxArrivalsPerPosition; j++) + arrival[i + j].cost = 0x40000000; + } + + arrival[nStartOffset * nMaxArrivalsPerPosition].cost = 0; + arrival[nStartOffset * nMaxArrivalsPerPosition].from_slot = -1; + arrival[nStartOffset * nMaxArrivalsPerPosition].rep_offset = *nCurRepMatchOffset; + + if (nInsertForwardReps) { + memset(visited + nStartOffset, 0, (nEndOffset - nStartOffset) * sizeof(salvador_visited)); + } + + salvador_arrival* cur_arrival; + for (i = nStartOffset, cur_arrival = &arrival[nStartOffset * nMaxArrivalsPerPosition]; i != nEndOffset; i++, cur_arrival += nMaxArrivalsPerPosition) { + salvador_arrival *pDestLiteralSlots = &cur_arrival[nMaxArrivalsPerPosition]; + int j, m; + + for (j = 0; j < nArrivalsPerPosition && cur_arrival[j].from_slot; j++) { + const int nNumLiterals = cur_arrival[j].num_literals + 1; + const int nCodingChoiceCost = cur_arrival[j].cost + 8 /* literal */ + (((nNumLiterals & (nNumLiterals - 1)) == 0) ? 2 : 0); + const int nScore = cur_arrival[j].score + 1; + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nCodingChoiceCost < pDestLiteralSlots[nArrivalsPerPosition - 1].cost || + (nCodingChoiceCost == pDestLiteralSlots[nArrivalsPerPosition - 1].cost && + nScore < pDestLiteralSlots[nArrivalsPerPosition - 1].score && + nRepOffset != pDestLiteralSlots[nArrivalsPerPosition - 1].rep_offset)) { + int exists = 0, n; + + for (n = 0; + pDestLiteralSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestLiteralSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestLiteralSlots[n].cost == nCodingChoiceCost && nScore >= pDestLiteralSlots[n].score; + n++) { + if (pDestLiteralSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestLiteralSlots[z].cost == nCodingChoiceCost; z++) { + if (pDestLiteralSlots[z].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestLiteralSlots[z].from_slot; z++) { + if (pDestLiteralSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestLiteralSlots[n + 1], + &pDestLiteralSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestLiteralSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = cur_arrival[j].rep_pos; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + } + } + } + } + } + + if (i == nStartOffset && (nBlockFlags & 1)) continue; + + const salvador_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT); + const unsigned short *match_depth = pCompressor->match_depth + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT); + const int nNumArrivalsForThisPos = j; + int nOverallMinRepLen = 0, nOverallMaxRepLen = 0; + + int nRepMatchArrivalIdx[(2 * NMAX_ARRIVALS_PER_POSITION) + 1]; + int nNumRepMatchArrivals = 0; + + if (i < nEndOffset) { + int nMaxRepLenForPos = nEndOffset - i; + if (nMaxRepLenForPos > LCP_MAX) + nMaxRepLenForPos = LCP_MAX; + + const unsigned char* pInWindowStart = pInWindow + i; + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLenForPos; + + for (j = 0; j < nNumArrivalsForThisPos; j++) { + if (cur_arrival[j].num_literals) { + const int nRepOffset = cur_arrival[j].rep_offset; + + if (i >= nRepOffset) { + if (pInWindowStart[0] == pInWindowStart[-nRepOffset]) { + if (nRepOffset) { + const int nLen0 = rle_len[i - nRepOffset]; + const int nLen1 = rle_len[i]; + const int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + const unsigned char* pInWindowAtPos = pInWindowStart + nMinLen; + + if (pInWindowAtPos > pInWindowMax) + pInWindowAtPos = pInWindowMax; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nRepOffset, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nRepOffset, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[0] == pInWindowAtPos[-nRepOffset]) + pInWindowAtPos++; + const int nCurRepLen = (const int)(pInWindowAtPos - pInWindowStart); + + if (nOverallMaxRepLen < nCurRepLen) + nOverallMaxRepLen = nCurRepLen; + nRepMatchArrivalIdx[nNumRepMatchArrivals++] = j; + nRepMatchArrivalIdx[nNumRepMatchArrivals++] = nCurRepLen; + } + } + } + } + } + } + nRepMatchArrivalIdx[nNumRepMatchArrivals] = -1; + + for (m = 0; m < NMATCHES_PER_INDEX && match[m].length; m++) { + int nOrigMatchLen = match[m].length; + const int nOrigMatchOffset = match[m].offset; + const unsigned int nOrigMatchDepth = match_depth[m]; + unsigned int d; + + if ((i + nOrigMatchLen) > nEndOffset) + nOrigMatchLen = nEndOffset - i; + + for (d = 0; d <= nOrigMatchDepth; d += (nOrigMatchDepth ? nOrigMatchDepth : 1)) { + const int nMatchLen = nOrigMatchLen - d; + const int nMatchOffset = nOrigMatchOffset - d; + + if (nInsertForwardReps) { + salvador_insert_forward_match(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, 0); + } + + int nNonRepMatchArrivalIdx = -1; + for (j = 0; j < nNumArrivalsForThisPos; j++) { + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset || cur_arrival[j].num_literals == 0) { + nNonRepMatchArrivalIdx = j; + break; + } + } + + int nStartingMatchLen, k; + + if (nNonRepMatchArrivalIdx >= 0) { + const int nNoRepmatchOffsetCost = cur_arrival[nNonRepMatchArrivalIdx].cost /* the actual cost of the literals themselves accumulates up the chain */ + OFFSET_COST(nMatchOffset); + const int nNoRepmatchScore = cur_arrival[nNonRepMatchArrivalIdx].score + 3; + + /* Insert non-repmatch candidate */ + + if (nMatchLen >= LEAVE_ALONE_MATCH_SIZE) { + nStartingMatchLen = nMatchLen; + } + else { + nStartingMatchLen = 2; + } + + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + salvador_arrival* pDestSlots = &cur_arrival[k * nMaxArrivalsPerPosition]; + const int nMatchLenCost = (k < 8192) ? salvador_cost_for_len[k - 1] : (salvador_get_match_varlen_size_norep(k - MIN_ENCODED_MATCH_SIZE) + TOKEN_SIZE /* token */); + const int nCodingChoiceCost = nMatchLenCost + nNoRepmatchOffsetCost; + + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 2].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 2].cost && + nNoRepmatchScore < pDestSlots[nArrivalsPerPosition - 2].score && + (nCodingChoiceCost != pDestSlots[nArrivalsPerPosition - 1].cost || nMatchOffset != pDestSlots[nArrivalsPerPosition - 1].rep_offset))) { + int exists = 0, n; + + for (n = 0; + pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestSlots[n].cost == nCodingChoiceCost && nNoRepmatchScore >= pDestSlots[n].score; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].cost == nCodingChoiceCost; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = nNonRepMatchArrivalIdx + 1; + pDestArrival->rep_offset = nMatchOffset; + pDestArrival->rep_pos = i; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nNoRepmatchScore; + } + } + } + } + } + } + + /* Insert repmatch candidates */ + + if (nMatchLen >= LEAVE_ALONE_MATCH_SIZE && nMatchLen > nOverallMinRepLen) { + nStartingMatchLen = nMatchLen; + } + else { + nStartingMatchLen = nOverallMinRepLen + 1; + } + + for (k = nStartingMatchLen; k <= (nOverallMaxRepLen < nMatchLen ? nOverallMaxRepLen : nMatchLen); k++) { + const int nMatchLenCost = (k < 8192) ? salvador_cost_for_len[k] : (salvador_get_match_varlen_size_rep(k - MIN_ENCODED_MATCH_SIZE) + TOKEN_SIZE /* token */); + salvador_arrival* pDestSlots = &cur_arrival[k * nMaxArrivalsPerPosition]; + int nCurRepMatchArrival; + + for (nCurRepMatchArrival = 0; (j = nRepMatchArrivalIdx[nCurRepMatchArrival]) >= 0; nCurRepMatchArrival += 2) { + if (nRepMatchArrivalIdx[nCurRepMatchArrival + 1] >= k) { + const int nRepCodingChoiceCost = cur_arrival[j].cost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost; + const int nScore = cur_arrival[j].score + 2; + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nRepCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nRepCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && + nScore < pDestSlots[nArrivalsPerPosition - 1].score && + nRepOffset != pDestSlots[nArrivalsPerPosition - 1].rep_offset)) { + int exists = 0, n; + + for (n = 0; + pDestSlots[n].cost < nRepCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestSlots[n].cost == nRepCodingChoiceCost && nScore >= pDestSlots[n].score; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].cost == nRepCodingChoiceCost; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nRepCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = i; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + } + } + } + } + else { + break; + } + } + } + + if (k <= LEAVE_ALONE_MATCH_SIZE) + nOverallMinRepLen = k; + else if (nOverallMaxRepLen == k) + nOverallMaxRepLen--; + } + } + + if (nOrigMatchLen >= 1280 && ((m + 1) >= NMATCHES_PER_INDEX || match[m + 1].length < 512)) + break; + } + } + + if (!nInsertForwardReps) { + const salvador_arrival* end_arrival = &arrival[(i * nMaxArrivalsPerPosition) + 0]; + salvador_final_match* pBestMatch = pCompressor->best_match - nStartOffset; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0 && (int)end_arrival->from_pos < nEndOffset) { + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + pBestMatch[end_arrival->from_pos].offset = (end_arrival->match_len) ? end_arrival->rep_offset : 0; + + end_arrival = &arrival[(end_arrival->from_pos * nMaxArrivalsPerPosition) + (end_arrival->from_slot - 1)]; + } + } +} + +/** + * Attempt to replace matches by literals when it makes the final bitstream smaller, and merge large matches + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nCurRepMatchOffset starting rep offset for this block + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int salvador_reduce_commands(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, const int *nCurRepMatchOffset, const int nBlockFlags) { + salvador_final_match* pBestMatch = pCompressor->best_match - nStartOffset; + int i; + int nNumLiterals = nBlockFlags & 1; + int nRepMatchOffset = *nCurRepMatchOffset; + int nDidReduce = 0; + + for (i = nStartOffset + (nBlockFlags & 1); i < nEndOffset; ) { + salvador_final_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < nEndOffset && + pBestMatch[i + 1].length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= nEndOffset && + (nNumLiterals != 0 || pBestMatch[i + 1].offset != nRepMatchOffset) && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize, nReducedLenSize; + + if (pBestMatch[i + 1].offset == nRepMatchOffset && nRepMatchOffset) { + nCurLenSize = salvador_get_match_varlen_size_rep(pBestMatch[i + 1].length - MIN_ENCODED_MATCH_SIZE); + } + else { + nCurLenSize = salvador_get_match_varlen_size_norep(pBestMatch[i + 1].length - MIN_ENCODED_MATCH_SIZE); + } + + if (nNumLiterals != 0 && pBestMatch[i + 1].offset == nRepMatchOffset && nRepMatchOffset) { + nReducedLenSize = salvador_get_match_varlen_size_rep(pBestMatch[i + 1].length + 1 - MIN_ENCODED_MATCH_SIZE); + } + else { + nReducedLenSize = salvador_get_match_varlen_size_norep(pBestMatch[i + 1].length + 1 - MIN_ENCODED_MATCH_SIZE); + } + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_ENCODED_MATCH_SIZE) { + if ((i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length >= MIN_ENCODED_MATCH_SIZE) { + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match */ + + if (nNumLiterals != 0 && nRepMatchOffset && pMatch->offset != nRepMatchOffset && (pBestMatch[nNextIndex].offset != pMatch->offset || pBestMatch[nNextIndex].offset == nRepMatchOffset || + OFFSET_COST(pMatch->offset) > OFFSET_COST(pBestMatch[nNextIndex].offset))) { + /* Check if we can get a missed backward repmatch */ + if (i >= nRepMatchOffset && + (i - nRepMatchOffset + pMatch->length) <= nEndOffset) { + int nMaxLen = 0; + while ((nMaxLen + 8) < pMatch->length && !memcmp(pInWindow + i - nRepMatchOffset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 8)) + nMaxLen += 8; + while ((nMaxLen + 4) < pMatch->length && !memcmp(pInWindow + i - nRepMatchOffset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 4)) + nMaxLen += 4; + while (nMaxLen < pMatch->length && pInWindow[i - nRepMatchOffset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + + if (nMaxLen >= 1) { + int nCurCommandSize, nReducedCommandSize; + + /* -- Original: Match with offset. Skip 'match with offset follows' bit. -- */ + + /* High bits of match offset */ + nCurCommandSize = salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_norep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + + /* Literals after command */ + nCurCommandSize += salvador_get_literals_varlen_size(nNextLiterals); + + /* -- Reduced: Rep match. Skip 'rep-match follows' bit. -- */ + + /* Match length */ + nReducedCommandSize = salvador_get_match_varlen_size_rep(nMaxLen - MIN_ENCODED_MATCH_SIZE); + + /* Literals after command */ + nReducedCommandSize += (pMatch->length - nMaxLen) << 3; + nReducedCommandSize += salvador_get_literals_varlen_size(nNextLiterals + (pMatch->length - nMaxLen)); + + if (nReducedCommandSize < nCurCommandSize) { + int j; + + /* Change to repmatch */ + + pMatch->offset = nRepMatchOffset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset && nRepMatchOffset != pBestMatch[nNextIndex].offset && nNextLiterals) { + /* Otherwise, try to gain a match forward as well */ + if (i >= pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= nEndOffset && pMatch->offset != nRepMatchOffset) { + int nMaxLen = 0; + while ((nMaxLen + 8) < pMatch->length && !memcmp(pInWindow + i - pBestMatch[nNextIndex].offset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 8)) + nMaxLen += 8; + while ((nMaxLen + 4) < pMatch->length && !memcmp(pInWindow + i - pBestMatch[nNextIndex].offset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 4)) + nMaxLen += 4; + while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + if (nMaxLen >= pMatch->length) { + /* Replace */ + pMatch->offset = pBestMatch[nNextIndex].offset; + nDidReduce = 1; + } + else if (nMaxLen >= 2) { + int nPartialSizeBefore, nPartialSizeAfter; + + nPartialSizeBefore = salvador_get_match_varlen_size_norep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + nPartialSizeBefore += OFFSET_COST(pMatch->offset); + nPartialSizeBefore += salvador_get_literals_varlen_size(nNextLiterals); + + nPartialSizeAfter = salvador_get_match_varlen_size_rep(nMaxLen - MIN_ENCODED_MATCH_SIZE); + nPartialSizeAfter += salvador_get_literals_varlen_size(nNextLiterals + (pMatch->length - nMaxLen)) + ((pMatch->length - nMaxLen) << 3); + + if (nPartialSizeAfter < nPartialSizeBefore) { + int j; + + /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but + * we have calculated that this is shorter */ + pMatch->offset = pBestMatch[nNextIndex].offset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pMatch->length < 9 /* Don't waste time considering large matches, they will always win over literals */) { + /* Calculate this command's current cost */ + + int nCurCommandSize = 0; + if (nNumLiterals != 0) { + nCurCommandSize += salvador_get_literals_varlen_size(nNumLiterals); + nCurCommandSize += (nNumLiterals << 3); + } + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0 && nRepMatchOffset) { + /* Rep match - don't include 'rep match follows' bit */ + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_rep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset - don't include 'match with offset follows' bit */ + + /* High bits of match offset */ + nCurCommandSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_norep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + } + + /* Calculate the next command's current cost */ + int nNextCommandSize = 0; + if (nNextLiterals != 0) { + nNextCommandSize += salvador_get_literals_varlen_size(nNextLiterals); + nNextCommandSize += (nNextLiterals << 3); + } + if (pMatch->offset && pBestMatch[nNextIndex].offset == pMatch->offset && nNextLiterals != 0) { + /* Rep match */ + nNextCommandSize += 1; /* rep-match follows */ + + /* Match length */ + nNextCommandSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset */ + nNextCommandSize += 1; /* match with offset follows */ + + /* High bits of match offset */ + nNextCommandSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nNextCommandSize += 7; + + /* Match length */ + nNextCommandSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + + const int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize; + + /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals */ + int nReducedCommandSize = (pMatch->length << 3); + nReducedCommandSize += salvador_get_literals_varlen_size(nNumLiterals + pMatch->length + nNextLiterals); + nReducedCommandSize += ((nNumLiterals + nNextLiterals) << 3); + + if (pBestMatch[nNextIndex].offset == nRepMatchOffset && (nNumLiterals + pMatch->length + nNextLiterals) != 0 && nRepMatchOffset) { + /* Rep match - don't include 'rep match follows' bit */ + + /* Match length */ + nReducedCommandSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset - don't include 'match with offset follows' bit */ + + /* High bits of match offset */ + nReducedCommandSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedCommandSize += 7; + + /* Match length */ + nReducedCommandSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + + if (nOriginalCombinedCommandSize >= nReducedCommandSize) { + /* Reduce */ + const int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + continue; + } + } + } + } + + if ((i + pMatch->length) < nEndOffset && pMatch->offset && pMatch->length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[i + pMatch->length].offset && + pBestMatch[i + pMatch->length].length >= MIN_ENCODED_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) >= pMatch->offset && + (i + pMatch->length) >= pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nNextIndex = i + pMatch->length + pBestMatch[i + pMatch->length].length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextIndex++; + nNextLiterals++; + } + + int nCurPartialSize = 0; + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0 && nRepMatchOffset) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_rep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nCurPartialSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + } + + /* Match with offset */ + nCurPartialSize += 1; /* match with offset */ + + /* High bits of match offset */ + nCurPartialSize += salvador_get_elias_size(((pBestMatch[i + pMatch->length].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[i + pMatch->length].length - MIN_ENCODED_MATCH_SIZE); + + if (nNextIndex < nEndOffset) { + if (pBestMatch[i + pMatch->length].offset && pBestMatch[nNextIndex].offset == pBestMatch[i + pMatch->length].offset && nNextLiterals != 0) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nCurPartialSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + } + + int nReducedPartialSize = 0; + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0 && nRepMatchOffset) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_rep(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_ENCODED_MATCH_SIZE); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nReducedPartialSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedPartialSize += 7; + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_norep(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_ENCODED_MATCH_SIZE); + } + + int nCannotReduce = 0; + if (nNextIndex < nEndOffset) { + if (pMatch->offset && pBestMatch[nNextIndex].offset == pMatch->offset && nNextLiterals != 0) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + else { + if (pBestMatch[nNextIndex].length >= MIN_ENCODED_MATCH_SIZE) { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nReducedPartialSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedPartialSize += 7; + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + } + else { + nCannotReduce = 1; + } + } + } + + if (nCurPartialSize >= nReducedPartialSize && !nCannotReduce) { + const int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + nDidReduce = 1; + continue; + } + } + + if (nNumLiterals != 0 && pMatch->offset != nRepMatchOffset && pMatch->length == MIN_ENCODED_MATCH_SIZE && nRepMatchOffset) { + if ((i + pMatch->length) < nEndOffset) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + /* Check if we can turn a match + a 1 byte rep match into all literals, and either reduce the output or keep it the same size */ + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && nNextLiterals != 0 && + pBestMatch[nNextIndex].length == 1 && + pBestMatch[nNextIndex].offset == pMatch->offset) { + int nNextNextIndex = nNextIndex + pBestMatch[nNextIndex].length; + int nNextNextLiterals = 0; + + while (nNextNextIndex < nEndOffset && pBestMatch[nNextNextIndex].length == 0) { + nNextNextLiterals++; + nNextNextIndex++; + } + + if (nNextNextIndex < nEndOffset && nNextNextLiterals != 0 && + pBestMatch[nNextNextIndex].length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[nNextNextIndex].offset != pBestMatch[nNextIndex].offset) { + int nCurCommandSize, nCurRepMatchSize, nReducedCommandSize; + + /* First command: match with offset */ + nCurCommandSize = salvador_get_literals_varlen_size(nNumLiterals); + nCurCommandSize += (nNumLiterals << 3); + + /* Match with offset */ + nCurCommandSize += 1; /* match with offset follows */ + + /* High bits of match offset */ + nCurCommandSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_norep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + + /* Second command: rep-match */ + nCurRepMatchSize = salvador_get_literals_varlen_size(nNextLiterals); + nCurRepMatchSize += (nNextLiterals << 3); + + nCurRepMatchSize += 1; /* rep-match follows */ + nCurRepMatchSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length - MIN_ENCODED_MATCH_SIZE); + + /* Combined commands as literals */ + nReducedCommandSize = salvador_get_literals_varlen_size(nNumLiterals + pMatch->length + nNextLiterals + pBestMatch[nNextIndex].length); + nReducedCommandSize += (nNumLiterals << 3); + nReducedCommandSize += (pMatch->length << 3); + nReducedCommandSize += (nNextLiterals << 3); + nReducedCommandSize += (pBestMatch[nNextIndex].length << 3); + + if ((nCurCommandSize + nCurRepMatchSize) >= nReducedCommandSize) { + const int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + pBestMatch[nNextIndex].length = 0; + nDidReduce = 1; + } + } + } + } + } + + nRepMatchOffset = pMatch->offset; + + i += pMatch->length; + nNumLiterals = 0; + } + else if (pMatch->length == 1) { + if (nNumLiterals != 0) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextLiterals != 0) { + int nCurPartialSize = salvador_get_literals_varlen_size(nNumLiterals); + nCurPartialSize += TOKEN_SIZE + salvador_get_match_varlen_size_rep(pMatch->length - MIN_ENCODED_MATCH_SIZE); + nCurPartialSize += salvador_get_literals_varlen_size(nNextLiterals); + + const int nReducedPartialSize = salvador_get_literals_varlen_size(nNumLiterals + 1 + nNextLiterals) + 8; + + if (nCurPartialSize >= nReducedPartialSize) { + pMatch->length = 0; + pMatch->offset = 0; + nDidReduce = 1; + continue; + } + } + } + + nNumLiterals = 0; + i++; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Emit a block of compressed data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_write_block(salvador_compressor* pCompressor, const unsigned char* pInWindow, const int nStartOffset, const int nEndOffset, unsigned char* pOutData, const int nMaxOutDataSize, int* nCurBitsOffset, int* nCurBitShift, int* nFinalLiterals, int* nCurRepMatchOffset, const int nBlockFlags) { + const salvador_final_match* pBestMatch = pCompressor->best_match - nStartOffset; + int nRepMatchOffset = *nCurRepMatchOffset; + int nOutOffset = 0; + const int nMaxOffset = pCompressor->max_offset; + const int nIsInverted = pCompressor->flags & FLG_IS_INVERTED; + const int nIsBackward = (pCompressor->flags & FLG_IS_BACKWARD) ? 1 : 0; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nIsFirstCommand = nBlockFlags & 1; + int i; + + for (i = nStartOffset; i < nEndOffset; ) { + const salvador_final_match* pMatch = pBestMatch + i; + + if (pMatch->length >= 2 || (pMatch->length == 1 && pMatch->offset == nRepMatchOffset && nNumLiterals != 0)) { + const int nMatchLen = pMatch->length; + const int nMatchOffset = pMatch->offset; + const int nEncodedMatchLen = nMatchLen - 2; + + if (nMatchOffset < MIN_OFFSET || nMatchOffset > nMaxOffset || nMatchOffset > MAX_OFFSET) + return -1; + + if (nIsFirstCommand && nNumLiterals == 0) { + /* The first block always starts with a literal */ + return -1; + } + + if (nNumLiterals != 0) { + /* Literals */ + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (!nIsFirstCommand) { + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literals follow */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + else { + /* The command code for the first literals is omitted */ + nIsFirstCommand = 0; + } + + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nNumLiterals, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if ((nOutOffset + nNumLiterals) > nMaxOutDataSize) + return -1; + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + } + + if (nMatchOffset == nRepMatchOffset && nNumLiterals != 0) { + /* Rep match */ + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* rep match */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write match length */ + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nEncodedMatchLen + 1 + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + else { + /* Match with offset */ + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match with offset */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write high bits of match offset */ + if (nIsInverted) + nOutOffset = salvador_write_inverted_elias_value(pOutData, nOutOffset, nMaxOutDataSize, ((nMatchOffset - 1) >> 7) + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + else + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, ((nMatchOffset - 1) >> 7) + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write low byte of match offset */ + if (nOutOffset >= nMaxOutDataSize) + return -1; + if (nIsBackward) + pOutData[nOutOffset++] = ((nMatchOffset - 1) & 0x7f) << 1; + else + pOutData[nOutOffset++] = (255 - ((nMatchOffset - 1) & 0x7f)) << 1; + + /* Write match length */ + if (nEncodedMatchLen) { + if (nIsBackward) + pOutData[nOutOffset - 1] |= 1; + nOutOffset = salvador_write_split_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nEncodedMatchLen + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + else { + if (!nIsBackward) + pOutData[nOutOffset - 1] |= 1; + } + } + + nNumLiterals = 0; + + if (nMatchOffset == nRepMatchOffset) + pCompressor->stats.num_rep_matches++; + else + pCompressor->stats.num_normal_matches++; + + nRepMatchOffset = nMatchOffset; + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += (long long)nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + const int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->stats.safe_dist < nCurSafeDist) + pCompressor->stats.safe_dist = nCurSafeDist; + + pCompressor->stats.commands_divisor++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + if (nBlockFlags & 2) { + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + *nFinalLiterals = 0; + + if (nNumLiterals != 0) { + /* Final Literals */ + + if (!nIsFirstCommand) { + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literals follow */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nNumLiterals, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if ((nOutOffset + nNumLiterals) > nMaxOutDataSize) + return -1; + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + } + + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match with offset */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if (nIsInverted) + nOutOffset = salvador_write_inverted_elias_value(pOutData, nOutOffset, nMaxOutDataSize, 256 /* EOD */, nIsBackward, nCurBitsOffset, nCurBitShift); + else + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, 256 /* EOD */, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + pCompressor->stats.num_eod++; + } + else { + *nFinalLiterals = nNumLiterals; + } + + *nCurRepMatchOffset = nRepMatchOffset; + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_optimize_and_write_block(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift, int *nFinalLiterals, int *nCurRepMatchOffset, const int nBlockFlags) { + const int nEndOffset = nPreviousBlockSize + nInDataSize; + int *rle_len = (int*)pCompressor->intervals /* reuse */; + int *first_offset_for_byte = pCompressor->first_offset_for_byte; + int *next_offset_for_pos = pCompressor->next_offset_for_pos; + int *offset_cache = pCompressor->offset_cache; + int i, nPosition; + + memset(pCompressor->best_match, 0, pCompressor->block_size * sizeof(salvador_final_match)); + + /* Supplement small matches */ + + memset(first_offset_for_byte, 0xff, sizeof(int) * 65536); + memset(next_offset_for_pos, 0xff, sizeof(int) * nInDataSize); + + for (nPosition = nPreviousBlockSize; nPosition < (nEndOffset - 1); nPosition++) { + next_offset_for_pos[nPosition - nPreviousBlockSize] = first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)]; + first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)] = nPosition; + } + + memset(offset_cache, 0xff, sizeof(int) * 2048); + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + salvador_match *match = pCompressor->match + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT); + unsigned short *match_depth = pCompressor->match_depth + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT); + int m = 0, nInserted = 0; + int nMatchPos; + + while (m < 15 && match[m].length) { + offset_cache[match[m].offset & 2047] = nPosition; + offset_cache[(match[m].offset - match_depth[m]) & 2047] = nPosition; + m++; + } + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < 15 && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + const int nMatchOffset = nPosition - nMatchPos; + + if (nMatchOffset <= pCompressor->max_offset) { + int nAlreadyExists = 0; + + if (offset_cache[nMatchOffset & 2047] == nPosition) { + int nExistingMatchIdx; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset || + (match[nExistingMatchIdx].offset - match_depth[nExistingMatchIdx]) == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + } + + if (!nAlreadyExists) { + int nMatchLen = 2; + while (nMatchLen < 128 && (nPosition + nMatchLen + 8) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 8)) + nMatchLen += 8; + while (nMatchLen < 128 && (nPosition + nMatchLen + 4) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 4)) + nMatchLen += 4; + while (nMatchLen < 128 && (nPosition + nMatchLen) < nEndOffset && pInWindow[nMatchPos + nMatchLen] == pInWindow[nPosition + nMatchLen]) + nMatchLen++; + match[m].length = nMatchLen; + match[m].offset = nMatchOffset; + match_depth[m] = 0; + m++; + nInserted++; + if (nInserted >= 15) + break; + } + } + else { + break; + } + } + } + + i = 0; + while (i < nEndOffset) { + int nRangeStartIdx = i; + const unsigned char c = pInWindow[nRangeStartIdx]; + + do { + i++; + } while (i < nEndOffset && pInWindow[i] == c); + + while (nRangeStartIdx < i) { + rle_len[nRangeStartIdx] = i - nRangeStartIdx; + nRangeStartIdx++; + } + } + + /* Compress and insert additional matches */ + salvador_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, 1 /* nInsertForwardReps */, nCurRepMatchOffset, NINITIAL_ARRIVALS_PER_POSITION, nBlockFlags); + + /* Supplement matches further */ + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + salvador_match* match = pCompressor->match + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT); + + if (match[0].length < 8) { + unsigned short* match_depth = pCompressor->match_depth + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT); + int m = 0, nInserted = 0; + int nMatchPos; + int nMaxForwardPos = nPosition + 2 + 1 + 3; + + if (nMaxForwardPos > (nEndOffset - 2)) + nMaxForwardPos = nEndOffset - 2; + + while (m < NMATCHES_PER_INDEX && match[m].length) { + offset_cache[match[m].offset & 2047] = nPosition; + offset_cache[(match[m].offset - match_depth[m]) & 2047] = nPosition; + m++; + } + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < NMATCHES_PER_INDEX && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + const int nMatchOffset = nPosition - nMatchPos; + + if (nMatchOffset <= pCompressor->max_offset) { + int nAlreadyExists = 0; + + if (offset_cache[nMatchOffset & 2047] == nPosition) { + int nExistingMatchIdx; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset || + (match[nExistingMatchIdx].offset - match_depth[nExistingMatchIdx]) == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + } + + if (!nAlreadyExists) { + int nForwardPos = nPosition + 2 + 1; + + if (nForwardPos >= nMatchOffset) { + int nGotMatch = 0; + + while (nForwardPos < nMaxForwardPos) { + if (pInWindow[nForwardPos] == pInWindow[nForwardPos - nMatchOffset]) { + nGotMatch = 1; + break; + } + nForwardPos++; + } + + if (nGotMatch) { + int nMatchLen = 2; + while (nMatchLen < 128 && (nPosition + nMatchLen + 8) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 8)) + nMatchLen += 8; + while (nMatchLen < 128 && (nPosition + nMatchLen + 4) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 4)) + nMatchLen += 4; + while (nMatchLen < 128 && (nPosition + nMatchLen) < nEndOffset && pInWindow[nMatchPos + nMatchLen] == pInWindow[nPosition + nMatchLen]) + nMatchLen++; + match[m].length = nMatchLen; + match[m].offset = nMatchOffset; + match_depth[m] = 0; + m++; + + salvador_insert_forward_match(pCompressor, pInWindow, nPosition, nMatchOffset, nPreviousBlockSize, nEndOffset, 8); + + nInserted++; + if (nInserted >= 9 || m >= NMATCHES_PER_INDEX) + break; + } + } + } + } + else { + break; + } + } + } + } + + /* Pick final matches */ + salvador_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, 0 /* nInsertForwardReps */, nCurRepMatchOffset, pCompressor->max_arrivals_per_position, nBlockFlags); + + /* Apply reduction and merge pass */ + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = salvador_reduce_commands(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, nCurRepMatchOffset, nBlockFlags); + nPasses++; + } while (nDidReduce && nPasses < 20); + + /* Write compressed block */ + + return salvador_write_block(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, pOutData, nMaxOutDataSize, nCurBitsOffset, nCurBitShift, nFinalLiterals, nCurRepMatchOffset, nBlockFlags); +} + +/* Forward declaration */ +static void salvador_compressor_destroy(salvador_compressor *pCompressor); + +/** + * Initialize compression context + * + * @param pCompressor compression context to initialize + * @param nBlockSize maximum size of input data (bytes to compress only) + * @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress) + * @param nMaxOffset maximum match offset to use (0 for default) + * @param nMaxArrivals maximum number of arrivals per position + * @param nFlags compression flags + * + * @return 0 for success, non-zero for failure + */ +static int salvador_compressor_init(salvador_compressor *pCompressor, const int nBlockSize, const int nMaxWindowSize, const size_t nMaxOffset, const int nMaxArrivals, const int nFlags) { + int nResult; + + nResult = divsufsort_init(&pCompressor->divsufsort_context); + pCompressor->intervals = NULL; + pCompressor->pos_data = NULL; + pCompressor->open_intervals = NULL; + pCompressor->match = NULL; + pCompressor->match_depth = NULL; + pCompressor->best_match = NULL; + pCompressor->arrival = NULL; + pCompressor->first_offset_for_byte = NULL; + pCompressor->next_offset_for_pos = NULL; + pCompressor->offset_cache = NULL; + if (nFlags & FLG_IS_BACKWARD) + pCompressor->flags = nFlags & (~FLG_IS_INVERTED); + else + pCompressor->flags = nFlags; + pCompressor->block_size = nBlockSize; + pCompressor->max_offset = nMaxOffset ? (int)nMaxOffset : MAX_OFFSET; + pCompressor->max_arrivals_per_position = nMaxArrivals; + + memset(&pCompressor->stats, 0, sizeof(pCompressor->stats)); + pCompressor->stats.min_match_len = -1; + pCompressor->stats.min_offset = -1; + pCompressor->stats.min_rle1_len = -1; + pCompressor->stats.min_rle2_len = -1; + + if (!nResult) { + pCompressor->intervals = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long)); + + if (pCompressor->intervals) { + pCompressor->pos_data = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long)); + + if (pCompressor->pos_data) { + pCompressor->open_intervals = (unsigned long long *)malloc((LCP_AND_TAG_MAX + 1) * sizeof(unsigned long long)); + + if (pCompressor->open_intervals) { + pCompressor->arrival = (salvador_arrival *)malloc((nBlockSize + 1) * nMaxArrivals * sizeof(salvador_arrival)); + + if (pCompressor->arrival) { + pCompressor->best_match = (salvador_final_match *)malloc(nBlockSize * sizeof(salvador_final_match)); + + if (pCompressor->best_match) { + pCompressor->match = (salvador_match *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(salvador_match)); + if (pCompressor->match) { + pCompressor->match_depth = (unsigned short *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(unsigned short)); + if (pCompressor->match_depth) { + pCompressor->first_offset_for_byte = (int*)malloc(65536 * sizeof(int)); + if (pCompressor->first_offset_for_byte) { + pCompressor->next_offset_for_pos = (int*)malloc(nBlockSize * sizeof(int)); + if (pCompressor->next_offset_for_pos) { + pCompressor->offset_cache = (int*)malloc(2048 * sizeof(int)); + if (pCompressor->offset_cache) { + return 0; + } + } + } + } + } + } + } + } + } + } + } + + salvador_compressor_destroy(pCompressor); + return 100; +} + +/** + * Clean up compression context and free up any associated resources + * + * @param pCompressor compression context to clean up + */ +static void salvador_compressor_destroy(salvador_compressor *pCompressor) { + divsufsort_destroy(&pCompressor->divsufsort_context); + + if (pCompressor->offset_cache) { + free(pCompressor->offset_cache); + pCompressor->offset_cache = NULL; + } + + if (pCompressor->next_offset_for_pos) { + free(pCompressor->next_offset_for_pos); + pCompressor->next_offset_for_pos = NULL; + } + + if (pCompressor->first_offset_for_byte) { + free(pCompressor->first_offset_for_byte); + pCompressor->first_offset_for_byte = NULL; + } + + if (pCompressor->match_depth) { + free(pCompressor->match_depth); + pCompressor->match_depth = NULL; + } + + if (pCompressor->match) { + free(pCompressor->match); + pCompressor->match = NULL; + } + + if (pCompressor->arrival) { + free(pCompressor->arrival); + pCompressor->arrival = NULL; + } + + if (pCompressor->best_match) { + free(pCompressor->best_match); + pCompressor->best_match = NULL; + } + + if (pCompressor->open_intervals) { + free(pCompressor->open_intervals); + pCompressor->open_intervals = NULL; + } + + if (pCompressor->pos_data) { + free(pCompressor->pos_data); + pCompressor->pos_data = NULL; + } + + if (pCompressor->intervals) { + free(pCompressor->intervals); + pCompressor->intervals = NULL; + } +} + +/** + * Compress one block of data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_compressor_shrink_block(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift, int *nFinalLiterals, int *nCurRepMatchOffset, const int nBlockFlags) { + int nCompressedSize; + + if (salvador_build_suffix_array(pCompressor, pInWindow, nPreviousBlockSize + nInDataSize)) + nCompressedSize = -1; + else { + if (nPreviousBlockSize) { + salvador_skip_matches(pCompressor, 0, nPreviousBlockSize); + } + salvador_find_all_matches(pCompressor, NMATCHES_PER_INDEX, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, nBlockFlags); + + nCompressedSize = salvador_optimize_and_write_block(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize, nCurBitsOffset, nCurBitShift, nFinalLiterals, nCurRepMatchOffset, nBlockFlags); + } + + return nCompressedSize; +} + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t salvador_get_max_compressed_size(const size_t nInputSize) { + return ((nInputSize + 65535) >> 16) * 128 + nInputSize; +} + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * @param nMaxOffset maximum match offset to use (0 for default) + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return actual compressed size, or -1 for error + */ +size_t salvador_compress(const unsigned char *pInputData, unsigned char *pOutBuffer, const size_t nInputSize, const size_t nMaxOutBufferSize, + const unsigned int nFlags, const size_t nMaxOffset, const size_t nDictionarySize, void(*progress)(long long nOriginalSize, long long nCompressedSize), salvador_stats *pStats) { + salvador_compressor compressor; + size_t nOriginalSize = 0; + size_t nCompressedSize = 0L; + int nResult; + int nMaxArrivals = NMAX_ARRIVALS_PER_POSITION; + int nError = 0; + const int nBlockSize = (nInputSize < BLOCK_SIZE) ? ((nInputSize < 1024) ? 1024 : (int)nInputSize) : BLOCK_SIZE; + const int nMaxOutBlockSize = (int)salvador_get_max_compressed_size(nBlockSize); + + nResult = salvador_compressor_init(&compressor, nBlockSize, nBlockSize * 2, nMaxOffset, nMaxArrivals, nFlags); + if (nResult != 0) { + return -1; + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + int nCurBitsOffset = 0, nCurBitShift = -1, nCurFinalLiterals = 0; + int nBlockFlags = 1; + int nCurRepMatchOffset = 1; + + if (nDictionarySize) { + nOriginalSize = (int)nDictionarySize; + nPreviousBlockSize = (int)nDictionarySize; + } + + while (nOriginalSize < nInputSize && !nError) { + int nInDataSize; + + nInDataSize = (int)(nInputSize - nOriginalSize); + if (nInDataSize > nBlockSize) + nInDataSize = nBlockSize; + + if (nInDataSize > 0) { + int nOutDataSize; + int nOutDataEnd = (int)(nMaxOutBufferSize - nCompressedSize); + + if (nOutDataEnd > nMaxOutBlockSize) + nOutDataEnd = nMaxOutBlockSize; + + if ((nOriginalSize + nInDataSize) >= nInputSize) + nBlockFlags |= 2; + nOutDataSize = salvador_compressor_shrink_block(&compressor, pInputData + nOriginalSize - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutBuffer + nCompressedSize, nOutDataEnd, + &nCurBitsOffset, &nCurBitShift, &nCurFinalLiterals, &nCurRepMatchOffset, nBlockFlags); + nBlockFlags &= (~1); + + if (nOutDataSize >= 0 && nCurFinalLiterals >= 0 && nCurFinalLiterals < nInDataSize) { + /* Write compressed block */ + + if (!nError) { + nInDataSize -= nCurFinalLiterals; + nOriginalSize += nInDataSize; + nCurFinalLiterals = 0; + nCompressedSize += nOutDataSize; + if (nCurBitShift != -1) + nCurBitsOffset -= nOutDataSize; + } + } + else { + nError = -1; + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + + if (!nError && nOriginalSize < nInputSize) { + if (progress) + progress(nOriginalSize, nCompressedSize); + } + } + + if (progress) + progress(nOriginalSize, nCompressedSize); + if (pStats) + *pStats = compressor.stats; + + salvador_compressor_destroy(&compressor); + + if (nError) { + return -1; + } + else { + return nCompressedSize; + } +} diff --git a/loader/tools/dali/salvador/src/shrink.h b/loader/tools/dali/salvador/src/shrink.h new file mode 100644 index 0000000..8c9c90b --- /dev/null +++ b/loader/tools/dali/salvador/src/shrink.h @@ -0,0 +1,175 @@ +/* + * shrink.h - compressor definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _SHRINK_H +#define _SHRINK_H + +#include "divsufsort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LCP_BITS 18 +#define TAG_BITS 4 +#define LCP_MAX ((1U<<(LCP_BITS - TAG_BITS)) - 1) +#define LCP_AND_TAG_MAX ((1U< BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; + +!cpu 6510 + +BITS_LEFT = 0 + +.depacker_dst = $01 +.smc_offsetd = .depacker_dst - (.dali_code_end - .dali_code_start) +!ifdef SFX_FAST { +DALI_FAST_SRC = lz_src - .smc_offsetd + 2 +DALI_FAST_DST = lz_dst - .smc_offsetd + 2 +DALI_FAST_SFX_ADDR = lz_sfx_addr - .smc_offsetd + 2 +DALI_FAST_DATA_END = lz_data_end - .smc_offsetd + 2 +DALI_FAST_DATA_SIZE_HI = lz_data_size_hi - .smc_offsetd + 2 +DALI_FAST_01 = lz_01 - .smc_offsetd + 2 +DALI_FAST_CLI = lz_cli - .smc_offsetd + 2 +} else { +DALI_SMALL_SRC = lz_src - .smc_offsetd + 2 +DALI_SMALL_DST = lz_dst - .smc_offsetd + 2 +DALI_SMALL_SFX_ADDR = lz_sfx_addr - .smc_offsetd + 2 +DALI_SMALL_DATA_END = lz_data_end - .smc_offsetd + 2 +DALI_SMALL_DATA_SIZE_HI = lz_data_size_hi - .smc_offsetd + 2 +} + +!macro get_lz_bit { + !if BITS_LEFT = 1 { + asl anc $08 would not hurt, but $9e hurts + !word 1602 + !byte $9e + !text "2061" + !byte $00,$00,$00 + + ;/!\ ATTENTION, the depacker just fits into ZP this way, if it gets larger, the copy routine will overwrite $00, as it is a 8-bit address sta + sei + +!ifdef SFX_FAST { + ;full zp code will be copied, but later less bytes will be copied back + ldx #<($100 + (.depacker_end - .restore_end)) + txs + +} + ldy #.depacker_end - .depacker_start +- +!ifdef SFX_FAST { + pha ;saved zp to stack down to $02 + lax <.depacker_dst - 1,y ;saves a byte, 2 byte compared to lda $0000,y +} + ldx .depacker_code - 1,y + stx <.depacker_dst - 1,y + dey + bne - + jmp .depack + + ;------------------ + ;depacker starts here + ;------------------ +.dali_code_end +.depacker_code +!pseudopc .depacker_dst { +.depacker_start + !byte $34 +lz_bits +!if BITS_LEFT = 1 { + !byte $40 +} else { + !byte $02 +} + +.depack +!ifdef SFX_FAST { +lz_01 = * + 1 + lda #$37 ;replace value for $01 in saved ZP on stack + pha +} +- ;copy data to end of ram ($ffff) + dey +lz_data_end = * + 1 +.src lda $beef,y +.dst sta $ff00,y + tya ;annoying, but need to copy from $ff ... $00 + bne - + + dec <.src + 2 +lz_data_size_hi = * + 1 + lda #$00 ;check for last page to copy + dcp <.dst + 2 + bne - + + ;ldy #$00 ;is already 0 + + ;------------------ + ;LITERAL + ;------------------ +.lz_start_over + lda #$01 ;we fall through this check on entry and start with literal + +get_lz_bit +!ifdef SFX_FAST { + bcc .literal + bcs .lz_new_offset ;after each match check for another match or literal? +- ;lz_length as inline + +get_lz_bit ;fetch payload bit + rol ;can also moved to front and executed once on start +.literal + +get_lz_bit + bcc - + + bne + + jsr lz_refill_bits + beq .lz_l_page ;happens very seldom, so let's do that with lz_l_page that also decrements lz_len_hi, it returns on c = 1, what is always true after jsr .lz_length ++ + tax +.lz_l_page_ +} else { + bcs .lz_new_offset ;after each match check for another match or literal? +.literal + jsr get_length + tax + beq .lz_l_page +.lz_l_page_ +} +cp_literal +lz_src = * + 1 + lda $beef,y ;looks expensive, but is cheaper than loop + sta (lz_dst),y + inc same as lda #$01 + +get_lz_bit + bcs .lz_new_offset ;either match with new offset or old offset + + ;------------------ + ;DO MATCH + ;------------------ +.lz_match + jsr get_length +!ifdef SFX_FAST { + sbc #$01 ;saves the sec and iny later on, if it results in a = $ff, no problem, we branch with the beq later on + sec +} else { +.lz_m_page_ + sbc #$01 ;saves the sec and iny later on, if it results in a = $ff, no problem, we branch with the beq later on + bcs .lz_match_ + dcp BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; + +!cpu 6510 + +BITS_LEFT = 0 + +.depacker = $01 +.smc_offsetd = .depacker - (.dali_code_end - .dali_code_start) +DALI_SRC = lz_src - .smc_offsetd + 2 +DALI_DST = lz_dst - .smc_offsetd + 2 +DALI_SFX_ADDR = lz_sfx_addr - .smc_offsetd + 2 +DALI_DATA_END = lz_data_end - .smc_offsetd + 2 +DALI_DATA_SIZE_HI = lz_data_size_hi - .smc_offsetd + 2 +DALI_01 = lz_01 - .smc_offsetd + 2 +DALI_CLI = lz_cli - .smc_offsetd + 2 + +!macro get_lz_bit { + !if BITS_LEFT = 1 { + asl (.data_end - .data) + 1 ;check for last page to copy + dcp <.dst + 2 + bne - + + ;ldy #$00 ;is already 0 + + ;------------------ + ;LITERAL + ;------------------ +.lz_start_over + lda #$01 ;we fall through this check on entry and start with literal + +get_lz_bit + bcs .lz_new_offset ;after each match check for another match or literal? +.literal + jsr .get_length + tax + beq .lz_l_page_ +.cp_literal +lz_src = * + 1 + lda .data,y ;looks expensive, but is cheaper than loop + sta (lz_dst),y + iny + dex + bne .cp_literal + + dey ;this way we force increment of lz_dst + 1 if y = 0 + tya + adc postpone until match that will happen necessarily later on? + bcc + + inc BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; + +!cpu 6510 + +BITS_LEFT = 0 + +.depacker = $01 +.smc_offsetd = .depacker - (.dali_code_end - .dali_code_start) +DALI_SMALL_SRC = lz_src - .smc_offsetd + 2 +DALI_SMALL_DST = lz_dst - .smc_offsetd + 2 +DALI_SMALL_SFX_ADDR = lz_sfx_addr - .smc_offsetd + 2 +DALI_SMALL_DATA_END = lz_data_end - .smc_offsetd + 2 +DALI_SMALL_DATA_SIZE_HI = lz_data_size_hi - .smc_offsetd + 2 + +!macro get_lz_bit { + !if BITS_LEFT = 1 { + asl (.data_end - .data) + 1 ;check for last page to copy + dcp <.dst + 2 + bne - + + ;ldy #$00 ;is already 0 + + ;------------------ + ;LITERAL + ;------------------ +.lz_start_over + lda #$01 ;we fall through this check on entry and start with literal + +get_lz_bit + bcs .lz_new_offset ;after each match check for another match or literal? +.literal + jsr .get_length + tax + beq .lz_l_page_ +.cp_literal +lz_src = * + 1 + lda .data,y ;looks expensive, but is cheaper than loop + sta (lz_dst),y + + inc $00) run serves as a + beq _lz_end_of_file ;sentinel +_lz_mrun_start + sta _lz_mcopy_len + + ldy #$ff ;The copy loop. This needs to be run + ;forwards since RLE-style matches can overlap the destination +!ifdef FAST_MATCH { + lsr ;Check bit 0 + bcc _lz_odd ;Odd or even number? Enter at right position of our loop that always copies two bytes in one go +} +_lz_mcopy + iny + lda (lz_match),y ;Copy one byte +!ifdef FAST_MATCH { + sta (lz_dst),y +_lz_odd + iny ;Next byte + lda (lz_match),y ;And another one +} + sta (lz_dst),y +_lz_mcopy_len = *+1 + cpy #$ff + bne _lz_mcopy + + tya ;Advance destination pointer +; sec + adc lz_dst+0 + sta lz_dst+0 + jmp _lz_mfinish + + + ;******** Offset coding tables ******** + + ;This length table is a bit funky. The idea here is to use the + ;value as the initial value of the shift register instead of + ;keeping a separate counter. + ;In other words we iterate until the leading one is shifted out. + ;Then afterwards the bit just below it (our new sign bit) is set + ;if the offset is shorter than 8-bits, and conversely it's + ;cleared if we need to fetch a separate low-byte + ;as well. + ;The fact that the sign bit is cleared as a flag is compensated + ;for in the lz_moff_adjust_hi table + +!ifndef DYNAMIC_TABLES { +_lz_moff_length + ;Long (>2 byte matches) + !byte %00011111 ;4 bits + !byte %00000011 ;7 bits + !byte %01011111 ;10 bits + !byte %00001011 ;13 bits + ;Short (2 byte matches) + !byte %01011111 ;10 bits + !byte %00000000 ;8 bits + !byte %00000111 ;6 bits + !byte %00111111 ;3 bits +_lz_moff_adjust_hi = *-2 + ;Long (>2 byte matches) +; !byte %11111111 ;1-16 (unreferenced) +; !byte %11111111 ;17-144 (unreferenced) + !byte %01111111 ;145-1168 + !byte %01111011 ;1169-9360 + ;Short (2 byte matches) + !byte %01111110 ;329-1352 + !byte %11111110 ;73-328 +; !byte %11111111 ;9-72 (unreferenced) +; !byte %11111111 ;1-8 (unreferenced) + +_lz_moff_adjust_lo = * - 1 + ;Long (>2 byte matches) + ;!byte %11111110 ;1-16 + !byte %11101110 ;17-144 + !byte %01101110 ;145-1168 + !byte %01101110 ;1169-9360 + ;Short (2 byte matches) + !byte %10110110 ;329-1352 + !byte %10110110 ;73-328 + !byte %11110110 ;9-72 + !byte %11111110 ;1-8 + ;******** Fetch some more bits to work with ******** +} + +lz_sector_ptr1 = *+1 +_lz_refill_bits ldy lz_sector,x + sty lz_bits +; sec + rol lz_bits + inx + beq lz_fetch_sector +_lz_end_of_file rts + +lz_fetch_sector + inc lz_sector_ptr1+1 + inc lz_sector_ptr2+1 + inc lz_sector_ptr3+1 + rts + +!ifdef DYNAMIC_TABLES { +_lz_moff_length +_lz_moff_adjust_lo = * + 8 +_lz_moff_adjust_hi = * + 16 +} diff --git a/loader/tools/doynamite1.1/krill/doynaxdecomp.s b/loader/tools/doynamite1.1/krill/doynaxdecomp.s new file mode 100644 index 0000000..b414983 --- /dev/null +++ b/loader/tools/doynamite1.1/krill/doynaxdecomp.s @@ -0,0 +1,411 @@ + +.macro SETDECOMPGETBYTE + sta toloadbt + $01 + sty toloadbt + $02 +.endmacro + +;------------------------------------------------------------------------------- +;Regular version of the Lempel-Ziv decompressor +;------------------------------------------------------------------------------- +lz_dst = decdestlo ;Decompression destination pointer. + ;Initialize this to whatever address + ;you want to decompress to + +lz_bits = DECOMPVARS + $00 ;Shift register. Initialized to $80 + ;for a new file + +lz_scratch = DECOMPVARS + $01 ;Temporary zeropage storage +lz_match = DECOMPVARS + $02 ;Source pointer for match + +lz_sector = $0400 ;The one-page buffer from which the + ;compressed data is actually read, + ;and which gets refilled by + ;lz_fetch_sector. + +.ifndef DYNLINK_EXPORT + .if GETC_API + .assert .lobyte(getcmem) <> .lobyte(getcmemfin), error, "Error: Invalid code optimization" + .assert .lobyte(getcmem) <> .lobyte(getcmemeof), error, "Error: Invalid code optimization" + .endif; GETC_API + .if BYTESTREAM + .if LOAD_VIA_KERNAL_FALLBACK + .assert .lobyte(getcmem) <> .lobyte(getckernal), error, "Error: Invalid code optimization" + .endif; LOAD_VIA_KERNAL_FALLBACK + .assert .lobyte(getcmem) <> .lobyte(getcload), error, "Error: Invalid code optimization" + .endif; BYTESTREAM +.endif; !DYNLINK_EXPORT + + +;------------------------------------------------------------------------------- +;This is the user's hook to replenish the sector buffer with some new bytes. +; +;A and Y are expected to be preserved while carry must remain set on exit. +;X should point to the first byte of the new data, e.g. zero for a full 256-byte +;page of data or two to skip past the sector and track links. +; +;When fetching from a larger in-memory array rather than a single sector buffer +;the lz_sector_ptr1..3 pointers will need to be patched up +;------------------------------------------------------------------------------- +lz_fetch_sector: + pha + sty save_y+1 + +.if BYTESTREAM + lda toloadbt + $01 + cmp #.lobyte(getcload) + beq isloading + + .if LOAD_VIA_KERNAL_FALLBACK + cmp #.lobyte(getcmem) + beq getblkfrommem + jsr getckernal + sta onebytebuffer + lda #.lobyte(onebytebuffer - $ff) + ldy #.hibyte(onebytebuffer - $ff) + ldx #$ff + bne setblockpntrs; jmp + +onebytebuffer: .byte $00 + .endif; LOAD_VIA_KERNAL_FALLBACK +.endif; BYTESTREAM + +getblkfrommem: ldx getcmemadr + $01 + lda #$00 + sta getcmemadr + $01 + ldy getcmemadr + $02 + inc getcmemadr + $02 + +.if BYTESTREAM + jmp setblockpntrs; jmp + +isloading: jsr maybegetblock + CHUNKCHECK + lda toloadbt + $01 + cmp #.lobyte(getcload) + bne getblkfrommem + + ldx YPNTRBUF + inx + bne :+; branch if first block + jsr toloadbt + ldx YPNTRBUF + SKIPBYTE +: dex + lda #$ff + sta YPNTRBUF +updateblkpntrs: lda getdbyte + $01 + ldy getdbyte + $02 +.endif; BYTESTREAM + +setblockpntrs: + sta lz_sector_ptr1 + $00 + sta lz_sector_ptr2 + $00 + sta lz_sector_ptr3 + $00 + sty lz_sector_ptr1 + $01 + sty lz_sector_ptr2 + $01 + sty lz_sector_ptr3 + $01 + +save_y: ldy #$00 + pla + sec + rts + +.if BYTESTREAM +maybegetblock: lda toloadbt + $01 + eor #.lobyte(getcload) + beq dogetblock + rts +dogetblock: + .if LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16) + ENABLE_IO_SPACE_Y + .endif; LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16) + BRANCH_IF_BLOCK_NOT_READY :++ + jsr getnewblk + lda toloadbt + $01 + cmp #.lobyte(getcmem) + bne :+ + jsr getcmem +: rts +: clc + jmp loadbytret +.endif; BYTESTREAM + +toloadbt: jmp getcmem + + +decompress: CHUNKENTRY + jsr toloadbt +storedadrl: sta lz_dst + $00 + jsr toloadbt +storedadrh: sta lz_dst + $01 + CHUNKSETUP + jsr lz_fetch_sector + ; fall through + +;------------------------------------------------------------------------------- +;This is the main lz_decrunch function which may be called to decompress an +;entire file. +; +;On entry and exit the X register points to the next available byte in the +;sector buffer, in ascending order from $00 to $ff. +;This implies that the initial sector must have already been fetched, and that a +;file ending with X wrapped to $00 will have needlessly fetched an extra sector +;(which may be taken advantage of when decoding a contiguous set of files.) +;------------------------------------------------------------------------------- + + ;******** Start the next match/literal run ******** + +lz_decrunch: sec ;This is the main entry point. Forcibly +_lz_type_refill: + jsr _lz_refill_bits ;fill up the the bit buffer on entry + bne _lz_type_cont ;(BRA) + + ;Wrap the high-byte of the destination pointer. +_lz_mfinish: bcc *+4 +_lz_maximum: inc lz_dst+1 ;This is also used by maximum length + ;literals needing an explicit type bit + +.if BYTESTREAM + jsr maybegetblock +.endif; BYTESTREAM + CHUNKCHECK + + ;Literal or match to follow? + asl lz_bits +_lz_type_cont: bcc _lz_do_match + beq lz_decrunch + + + ;******** Process literal run ******** + + lda #%00000000 ;Decode run length +_lz_lrun_loop: rol + asl lz_bits + bcs _lz_lrun_test +_lz_lrun_back: asl lz_bits + bne _lz_lrun_loop + + jsr _lz_refill_bits + bne _lz_lrun_loop ;(BRA) + +_lz_lrun_test: bne _lz_lrun_gotten + jsr _lz_refill_bits + bcc _lz_lrun_back + +_lz_lrun_gotten: + sta _lz_copy_cnt+1 ;Store LSB of run-length + ldy #$00 +_lz_lcopy: +lz_sector_ptr2 = *+1 ;Copy the literal data. Note the + lda lz_sector,x + inx + bne *+5 + jsr lz_fetch_sector ;Grab a new sector for the literal loop + sta (lz_dst),y + iny +_lz_copy_cnt: cpy #$00 + bne _lz_lcopy + + ;Time to advance the destination pointer. + ;Maximum run length literals exit here as a type-bit needs + ;to be fetched afterwards + tya + beq _lz_maximum + clc + adc lz_dst+0 + sta lz_dst+0 + bcc *+4 + inc lz_dst+1 + +.if BYTESTREAM + jsr maybegetblock +.endif; BYTESTREAM + CHUNKCHECK + + ;One literal run following another only makes sense if the + ;first run is of maximum length and had to be split. As that + ;case has been taken care of we can safely omit the type bit + ;here + + + ;******** Process match ******** + +_lz_do_match: lda #%00100000 ;Determine offset length by a two-bit +_lz_moff_range: asl lz_bits ;prefix combined with the first run + bne *+5 ;length bit (where a one identifies + jsr _lz_refill_bits ;a two-byte match). + rol ;The rest of the length bits will + bcc _lz_moff_range ;then follow *after* the offset data + + tay + lda _lz_moff_length,y + beq _lz_moff_far + +_lz_moff_loop: asl lz_bits ;Load partial offset byte + bne *+9 + sty lz_scratch + jsr _lz_refill_bits + ldy lz_scratch + + rol + bcc _lz_moff_loop + + bmi _lz_moff_near + +_lz_moff_far: sta lz_scratch ;Save the bits we just read as the + ;high-byte + +lz_sector_ptr3 = *+1 + lda lz_sector,x ;For large offsets we can load the + inx ;low-byte straight from the stream + bne *+5 ;without going throught the shift + jsr lz_fetch_sector ;register + +; sec + adc _lz_moff_adjust_lo,y + bcs _lz_moff_pageok + dec lz_scratch + sec +_lz_moff_pageok: + adc lz_dst+0 + sta lz_match+0 + + lda lz_scratch + adc _lz_moff_adjust_hi,y + sec + bcs _lz_moff_join ;(BRA) + +_lz_moff_near: +; sec ;Special case handling of <8 bit offsets. + adc _lz_moff_adjust_lo,y;We may can safely ignore the MSB from +; sec ;the base adjustment table as the + adc lz_dst+0 ;maximum base (for a 4/5/6/7 bit + sta lz_match+0 ;length sequence) is 113 + lda #$ff +_lz_moff_join: adc lz_dst+1 + sta lz_match+1 + + cpy #$04 ;Get any remaning run length bits + lda #%00000001 + bcs _lz_mrun_gotten + +_lz_mrun_loop: asl lz_bits + bne *+5 + jsr _lz_refill_bits + rol + asl lz_bits + bcc _lz_mrun_loop + bne _lz_mrun_gotten + jsr _lz_refill_bits + bcc _lz_mrun_loop + +_lz_mrun_gotten: + tay ;A 257-byte (=>$00) run serves as a + beq _lz_end_of_file ;sentinel + + sta _lz_mcopy_len + + ldy #$ff ;The copy loop. This needs to be run + lsr + bcc _lz_odd +_lz_mcopy: iny ;forwards since RLE-style matches can + lda (lz_match),y + sta (lz_dst),y +_lz_odd: + iny + lda (lz_match),y + sta (lz_dst),y +_lz_mcopy_len = *+1 + cpy #$ff + bne _lz_mcopy + + tya ;Advance destination pointer +; sec + adc lz_dst+0 + sta lz_dst+0 + jmp _lz_mfinish + + + ;******** Fetch some more bits to work with ******** + +lz_sector_ptr1 = *+1 +_lz_refill_bits: + ldy lz_sector,x + sty lz_bits + inx + bne *+5 + jsr lz_fetch_sector +; sec + rol lz_bits + rts + +_lz_end_of_file: + ; housekeeping to finish decompression +.if BYTESTREAM + .if LOAD_VIA_KERNAL_FALLBACK + lda toloadbt + $01 + cmp #.lobyte(getckernal) + beq decompfinished + .endif; LOAD_VIA_KERNAL_FALLBACK + stx YPNTRBUF +.endif; BYTESTREAM + stx getcmemadr + $01 + dec getcmemadr + $02 +.if GETC_API + lda getcmemadr + $01 + sta getcmemfin + $01 + lda getcmemadr + $02 + sta getcmemfin + $02 +.endif; GETC_API + + ; decompression finished +decompfinished: CHUNKEOF + rts + + + ;******** Offset coding tables ******** + + ;This length table is a bit funky. The idea here is to use the + ;value as the initial value of the shift register instead of + ;keeping a separate counter. + ;In other words we iterate until the leading one is shifted out. + ;Then afterwards the bit just below it (our new sign bit) is set + ;if the offset is shorter than 8-bits, and conversely it's + ;cleared if we need to fetch a separate low-byte + ;as well. + ;The fact that the sign bit is cleared as a flag is compensated + ;for in the lz_moff_adjust_hi table + +_lz_moff_length: + ;Long (>2 byte matches) + .byte %00011111 ;4 bits + .byte %00000011 ;7 bits + .byte %01011111 ;10 bits + .byte %00001011 ;13 bits + ;Short (2 byte matches) + .byte %01011111 ;10 bits + .byte %00000000 ;8 bits + .byte %00000111 ;6 bits + .byte %00111111 ;3 bits +_lz_moff_adjust_lo: + ;Long (>2 byte matches) + .byte %11111110 ;1-16 + .byte %11101110 ;17-144 + .byte %01101110 ;145-1168 + .byte %01101110 ;1169-9360 + ;Short (2 byte matches) + .byte %10110110 ;329-1352 + .byte %10110110 ;73-328 + .byte %11110110 ;9-72 + .byte %11111110 ;1-8 +_lz_moff_adjust_hi = *-2 + ;Long (>2 byte matches) +; .byte %11111111 ;1-16 (unreferenced) +; .byte %11111111 ;17-144 (unreferenced) + .byte %01111111 ;145-1168 + .byte %01111011 ;1169-9360 + ;Short (2 byte matches) + .byte %01111110 ;329-1352 + .byte %11111110 ;73-328 +; .byte %11111111 ;9-72 (unreferenced) +; .byte %11111111 ;1-8 (unreferenced) diff --git a/loader/tools/doynamite1.1/lz.c b/loader/tools/doynamite1.1/lz.c new file mode 100644 index 0000000..3a8a5af --- /dev/null +++ b/loader/tools/doynamite1.1/lz.c @@ -0,0 +1,1377 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + false, + true +} bool; + +#define USE_LITERAL_RUNS 1 +#define VERIFY_COST_MODEL 0 +#define DEFAULT_LENGTHS "3/6/8/10:4/7/10/13" + +enum { + RUN_LIMIT = 0x100, + OFFSET_LENGTH_LIMIT = 15 +}; + +static const char decruncher[] = { +0x01,0x08,0x0b,0x08,0x39,0x05,0x9e,0x32,0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xe6, +0x01,0xa2,0xff,0x9a,0xe8,0xbd,0x37,0x08,0x9d,0xc2,0x00,0xe8,0xd0,0xf7,0xa0,0x01, +0xca,0xbd,0x1a,0x08,0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x22,0x08,0xce,0x25,0x08, +0x88,0xd0,0xed,0xa2,0x1a,0x4c,0xc6,0x00,0x00,0x00,0x00,0x00,0x38,0x20,0x8e,0x01, +0xd0,0x06,0x90,0x02,0xe6,0xfc,0x06,0xc4,0x90,0x3a,0xf0,0xf1,0xa9,0x00,0x2a,0x06, +0xc4,0xb0,0x09,0x06,0xc4,0xd0,0xf7,0x20,0x8e,0x01,0xd0,0xf2,0xd0,0x05,0x20,0x8e, +0x01,0x90,0xf0,0x85,0xff,0xa0,0x00,0xbd,0x00,0x00,0xe8,0xd0,0x03,0x20,0x98,0x01, +0x99,0x00,0x40,0xc8,0xc0,0x00,0xd0,0xef,0x98,0xf0,0xc9,0x18,0x65,0xfb,0x85,0xfb, +0x90,0x02,0xe6,0xfc,0xa9,0x20,0x06,0xc4,0xd0,0x03,0x20,0x8e,0x01,0x2a,0x90,0xf6, +0xa8,0xb9,0xa7,0x01,0xf0,0x10,0x06,0xc4,0xd0,0x07,0x84,0xc5,0x20,0x8e,0x01,0xa4, +0xc5,0x2a,0x90,0xf2,0x30,0x1f,0x85,0xc5,0xbd,0x00,0x00,0xe8,0xd0,0x03,0x20,0x98, +0x01,0x79,0xaf,0x01,0xb0,0x03,0xc6,0xc5,0x38,0x65,0xfb,0x85,0xc2,0xa5,0xc5,0x79, +0xb7,0x01,0x38,0xb0,0x09,0x79,0xaf,0x01,0x65,0xfb,0x85,0xc2,0xa9,0xff,0x65,0xfc, +0x85,0xc3,0xc0,0x04,0xa9,0x01,0xb0,0x16,0x06,0xc4,0xd0,0x03,0x20,0x8e,0x01,0x2a, +0x06,0xc4,0x90,0xf4,0xd0,0x05,0x20,0x8e,0x01,0x90,0xed,0xa8,0xf0,0x29,0x8d,0x83, +0x01,0xa0,0xff,0xc8,0xb1,0xc2,0x91,0xfb,0xc0,0xff,0xd0,0xf7,0x98,0x65,0xfb,0x85, +0xfb,0x4c,0xcc,0x00,0xbc,0x00,0x00,0x84,0xc4,0x26,0xc4,0xe8,0xd0,0x08,0xee,0x90, +0x01,0xe6,0xf3,0xee,0x34,0x01,0x60,0xc6,0x01,0x58,0x4c +}; + +// Some definitions for compiler independence +#ifdef _MSC_VER +# include +# include +# define alloca _alloca +# define inline __forceinline +#else +# include +# include +#endif + +#undef min +#define remainder remainder_ + +// The main crunching structure +typedef struct { + signed short match_length; + unsigned short match_offset; + + union { + signed hash_link; + unsigned cumulative_cost; + }; +} lz_info; + +typedef struct { + unsigned char *src_data; + unsigned src_begin; + unsigned src_end; + signed margin; + + FILE *dst_file; + unsigned dst_bits; + unsigned dst_used; + + lz_info *info; + + signed hash_table[0x100]; + unsigned char dst_literals[RUN_LIMIT * 2]; + + // Some informational counters + struct { + unsigned output_size; + unsigned short_freq[4]; + unsigned long_freq[4]; + unsigned literal_bytes; + unsigned literal_runs; + unsigned match_bytes; + unsigned match_count; + unsigned offset_distance; + } stats; +} lz_context; + +// A bit of global configuration data +typedef struct { + unsigned bits; + unsigned base; + signed limit; +} offset_length_t; + +static offset_length_t cfg_short_offset[4]; +static offset_length_t cfg_long_offset[4]; +#define cfg_short_limit (cfg_short_offset[3].limit) +#define cfg_long_limit (cfg_long_offset[3].limit) + +static bool cfg_per_page = false; + + +/****************************************************************************** + * Various utility functions and bithacks + ******************************************************************************/ +#define countof(n) (sizeof(n) / sizeof *(n)) + +static inline unsigned _log2(unsigned value) { +# ifdef __GNUC__ + enum { WORD_BITS = sizeof(unsigned) * CHAR_BIT }; + + return (WORD_BITS - 1) ^ __builtin_clz(value); +# else + signed bits = -1; + + do + ++bits; + while(value >>= 1); + + return bits; +# endif +} + +static inline bool wraps(unsigned cursor, unsigned length, unsigned limit) { + return ((cursor + length) ^ cursor) >= limit; +} + +static inline unsigned remainder(signed cursor, signed window) { + return -(cursor | -window); +} + +static inline unsigned min(unsigned a, unsigned b) { + return (a < b) ? a : b; +} + +#ifdef _MSC_VER +__declspec(noreturn) +#elif defined(__GNUC__) +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +#endif +static void +#ifdef _MSC_VER +__cdecl +#endif +fatal(const char *format, ...) { + va_list args; + + va_start(args, format); + fputs("error: ", stderr); + vfprintf(stderr, format, args); + fputc('\n', stderr); + va_end(args); + + exit(EXIT_FAILURE); +} + + + +/****************************************************************************** + * Manage the output stream + ******************************************************************************/ +static inline void _output_doflush(lz_context *ctx) { + putc(ctx->dst_bits, ctx->dst_file); + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +static inline void output_open(lz_context *ctx, const char *name) { + if(ctx->dst_file = fopen(name, "wb"), !ctx->dst_file) + fatal("error: cannot create '%s'", name); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +static inline void output_close(lz_context *ctx) { + if(ctx->dst_bits != 1) { + while(ctx->dst_bits < 0x100) + ctx->dst_bits <<= 1; + + putc(ctx->dst_bits, ctx->dst_file); + } + + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + ctx->stats.output_size = ftell(ctx->dst_file); + fclose(ctx->dst_file); +} + +static inline void output_bit(lz_context *ctx, unsigned bit) { + if(ctx->dst_bits >= 0x100) + _output_doflush(ctx); + + ctx->dst_bits <<= 1; + ctx->dst_bits += bit & 1; +} + +static inline void output_literal(lz_context *ctx, unsigned value) { + ctx->dst_literals[ctx->dst_used++] = value; +} + +static inline unsigned output_bitsize(lz_context *ctx) { + unsigned total; + unsigned bitbuffer; + + total = ftell(ctx->dst_file); + total += ctx->dst_used; + total <<= 3; + + for(bitbuffer = ctx->dst_bits; bitbuffer > 1; bitbuffer >>= 1) + ++total; + + return total; +} + + +/****************************************************************************** + * Read file into memory and allocate per-byte buffers + ******************************************************************************/ +void read_input(lz_context *ctx, const char *name, bool is_cbm) { + FILE *file; + signed length; + unsigned origin; + + if(file = fopen(name, "rb"), !file) + fatal("unable to open '%s'", name); + +# ifdef _MSC_VER + length = _filelength(_fileno(file)); +# else + { + struct stat stat; + stat.st_size = 0; + fstat(fileno(file), &stat); + length = stat.st_size; + } +# endif + + if(length <= 0) + fatal("cannot determine length of '%s'", name); + + { + // Give us a sentinel for the info structure and prevent two-byte + // hashing from overrunning the buffer + unsigned count = length + 1; + + ctx->info = malloc(count * + (sizeof *ctx->info + sizeof *ctx->src_data)); + ctx->src_data = (void *) &ctx->info[count]; + + if(!ctx->info) + fatal("cannot allocate memory buffer"); + + if(fread(ctx->src_data, length, 1, file) != 1) + fatal("cannot read '%s'", name); + } + + // Deal with the PRG file header. We don't write the loading + // address back out to compressed file, however we *do* need to + // consider the decompression address when deciding whether a + // run crosses a page or window boundary + origin = 0; + + if(is_cbm) { + length -= 2; + + if(length < 0) { + fatal("CBM .prg file is too short to " + "fit a 2-byte load address"); + } + + origin = *ctx->src_data++; + origin += *ctx->src_data++ << 8; + } + + ctx->info -= origin; + ctx->src_data -= origin; + ctx->src_begin = origin; + ctx->src_end = origin + length; +} + +// Cut out a specific part of the file to compress +static inline void cut_input(lz_context *ctx, unsigned origin, unsigned limit) { + ctx->src_begin += origin; + ctx->src_end = min(ctx->src_end, ctx->src_begin + limit); + if(ctx->src_begin > ctx->src_end) + fatal("no data in address range %d %d", ctx->src_begin, ctx->src_end); +} + + +/****************************************************************************** + * Try to figure out what matches would be the most beneficial + ******************************************************************************/ +static inline unsigned costof_run(unsigned run) { + return _log2(run) * 2 + 1; +} + +#if USE_LITERAL_RUNS +static inline unsigned costof_literals(unsigned address, unsigned length) { + unsigned cost; + + cost = length * 8; + cost += costof_run(length); + + // Implicit matches cannot be used after the end of a page + // when doing per-page rendering, hence an extra bit is + // needed here + if(cfg_per_page) { + cost += wraps(address, length, RUN_LIMIT); + // A type bit is still always needed after maximum length + // run since another run may follow + } else if(length == RUN_LIMIT) + ++cost; + return cost; +} +#else +enum { COSTOF_LITERAL = 9 }; +#endif + +static inline unsigned costof_match(const offset_length_t *class, signed offset, unsigned length) { + unsigned cost = 3; + + while(offset > class->limit) + ++class; + cost += class->bits; + + return cost + costof_run(length - 1); +} + +static inline lz_info optimal_parsing_literal(const lz_info *info, unsigned cursor) { + signed length; + unsigned cost; + lz_info result; + +# if USE_LITERAL_RUNS + length = -info[cursor + 1].match_length; + + if(length > 0 && length < RUN_LIMIT) + cost = info[cursor + ++length].cumulative_cost; + else +# endif + { + cost = info[cursor + 1].cumulative_cost; + length = 1; + } + +# if USE_LITERAL_RUNS + cost += costof_literals(cursor, length); +# else + cost += COSTOF_LITERAL; +# endif + + result.match_length = -length; + result.cumulative_cost = cost; + + return result; +} + +static inline lz_info optimal_parsing ( + const lz_info *info, + unsigned cursor, + signed match_offset, + unsigned match_length, + unsigned match_limit, + lz_info best_match +) { + unsigned cost; + + if(match_length == 2) { + if(match_offset <= cfg_short_limit) { + cost = costof_match(cfg_short_offset, match_offset, match_length); + goto try_short_match; + } else if(++match_length > match_limit) + return best_match; + } + + do { + cost = costof_match(cfg_long_offset, match_offset, match_length); +try_short_match: + cost += info[cursor + match_length].cumulative_cost; + + if(cost < best_match.cumulative_cost) { + best_match.match_offset = match_offset; + best_match.match_length = match_length; + best_match.cumulative_cost = cost; + } + } while(++match_length <= match_limit); + + return best_match; +} + + + +/****************************************************************************** + * Determine the longest match for every position of the file + ******************************************************************************/ +static inline signed *hashof(lz_context *ctx, unsigned a, unsigned b) { + static const unsigned char random[] = { + 0x17, 0x80, 0x95, 0x4f, 0xc7, 0xd1, 0x15, 0x13, + 0x91, 0x57, 0x0f, 0x47, 0xd0, 0x59, 0xab, 0xf0, + 0xa7, 0xf5, 0x36, 0xc0, 0x24, 0x9c, 0xed, 0xfd, + 0xd4, 0xf3, 0x51, 0xb4, 0x8c, 0x97, 0xa3, 0x58, + 0xcb, 0x61, 0x78, 0xb1, 0x3e, 0x7e, 0xfb, 0x41, + 0x39, 0xa6, 0x8e, 0x10, 0xa1, 0xba, 0x62, 0xcd, + 0x94, 0x02, 0x0d, 0x2b, 0xdb, 0xd7, 0x44, 0x16, + 0x29, 0x4d, 0x68, 0x0a, 0x6b, 0x6c, 0xa2, 0xf8, + 0xc8, 0x9f, 0x25, 0xca, 0xbd, 0x4a, 0xc2, 0x35, + 0x53, 0x1c, 0x40, 0x04, 0x76, 0x43, 0xa9, 0xbc, + 0x46, 0xeb, 0x99, 0xe9, 0xf6, 0x5e, 0x8f, 0x8a, + 0xf1, 0x5d, 0x21, 0x33, 0x0b, 0x82, 0xdf, 0x52, + 0xea, 0x27, 0x22, 0x9a, 0x6f, 0xad, 0xe5, 0x83, + 0x11, 0xbe, 0xa4, 0x85, 0x1d, 0xb3, 0x77, 0xf4, + 0xef, 0xb7, 0xf2, 0x03, 0x64, 0x6d, 0x1b, 0xee, + 0x72, 0x08, 0x66, 0xc6, 0xc1, 0x06, 0x56, 0x81, + 0x55, 0x60, 0x70, 0x8d, 0x23, 0xb2, 0x65, 0x5b, + 0xff, 0x4c, 0xb9, 0x7a, 0xd6, 0xe6, 0x19, 0x9b, + 0xb5, 0x49, 0x7d, 0xd8, 0x45, 0x1a, 0x84, 0x32, + 0xdd, 0xbf, 0x9e, 0x2f, 0xd2, 0xec, 0x92, 0x0e, + 0xe8, 0x7c, 0x7f, 0x00, 0x86, 0xde, 0xb6, 0xcf, + 0x05, 0x69, 0xd5, 0x37, 0xe4, 0x30, 0x3c, 0xe1, + 0x4b, 0xaa, 0x3b, 0x2d, 0xda, 0x5c, 0xcc, 0x67, + 0x20, 0xb0, 0x6a, 0x1f, 0xf9, 0x01, 0xac, 0x2e, + 0x71, 0xf7, 0xfc, 0x3f, 0x42, 0xd3, 0xbb, 0xa8, + 0x38, 0xce, 0x12, 0x96, 0xe2, 0x14, 0x87, 0x4e, + 0x63, 0x07, 0xae, 0xdc, 0xa5, 0xc9, 0x0c, 0x90, + 0xe7, 0xd9, 0x09, 0x2a, 0xc4, 0x3d, 0x5a, 0x34, + 0x8b, 0x88, 0x98, 0x48, 0xfa, 0xc3, 0x26, 0x75, + 0xfe, 0xa0, 0x7b, 0x50, 0x2c, 0x89, 0x18, 0x9d, + 0x3a, 0x73, 0x6e, 0x5f, 0xc5, 0xaf, 0xb8, 0x74, + 0x93, 0xe3, 0x79, 0x28, 0xe0, 0x1e, 0x54, 0x31 + }; + + size_t bucket = random[a] ^ b; + return &ctx->hash_table[bucket]; +} + +static inline void generate_hash_table(lz_context *ctx) { + unsigned cursor; + + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + for(cursor = 0; cursor < countof(ctx->hash_table); ++cursor) + ctx->hash_table[cursor] = INT_MIN; + + for(cursor = ctx->src_begin; cursor != src_end; ++cursor) { + signed *hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + info[cursor].hash_link = *hash_bucket; + *hash_bucket = cursor; + } +} + +static inline void find_matches(lz_context *ctx, unsigned window) { + const unsigned src_begin = ctx->src_begin; + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + unsigned offset_limit = min(window, cfg_long_limit); + unsigned cursor = ctx->src_end; + + info[cursor].cumulative_cost = 0; + + while(cursor != src_begin) { + unsigned match_length; + signed cursor_limit; + unsigned length_limit; + signed *hash_bucket; + signed hash_link; + lz_info best_match; + + --cursor; + + match_length = 1; + cursor_limit = cursor - offset_limit; + + length_limit = RUN_LIMIT; + length_limit = min(length_limit, remainder(cursor, window)); + length_limit = min(length_limit, src_end - cursor); + + hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + assert((unsigned) *hash_bucket == cursor); + hash_link = info[cursor].hash_link; + *hash_bucket = hash_link; + + best_match = optimal_parsing_literal(info, cursor); + + while(hash_link >= cursor_limit) { + unsigned match_limit = remainder(hash_link, window); + match_limit = min(match_limit, length_limit); + + if(match_length != match_limit) { + unsigned i = match_length + 1; + + if(!memcmp(&src_data[cursor], &src_data[hash_link], i)) { + for(; i != match_limit; ++i) { + if(src_data[cursor + i] != src_data[hash_link + i]) + break; + } + + assert(i <= match_limit); + + best_match = optimal_parsing ( + info, + cursor, + cursor - hash_link, + match_length + 1, + i, + best_match + ); + + match_length = i; + + if(match_length == RUN_LIMIT) + break; + } + } + + hash_link = info[hash_link].hash_link; + } + + info[cursor] = best_match; + } +} + + +/****************************************************************************** + * Write the generated matches and literal runs + ******************************************************************************/ +#if USE_LITERAL_RUNS +static inline void encode_literals ( + lz_context *ctx, + unsigned cursor, + unsigned length +) { + signed bit; + const unsigned char *data; + unsigned start = length; + + ctx->stats.literal_bytes += length; + ++ctx->stats.literal_runs; + + bit = _log2(length); + while(--bit >= 0) { + output_bit(ctx, 0); + output_bit(ctx, length >> bit); + } + + output_bit(ctx, 1); + + data = &ctx->src_data[cursor]; + do + output_literal(ctx, data[start - length--]); + while(length); +} +#endif + +static inline void encode_match ( + lz_context *ctx, + signed offset, + unsigned length +) { + unsigned offset_bits; + unsigned offset_prefix; + const offset_length_t *offset_class; + signed length_bit; + + ++ctx->stats.match_count; + ctx->stats.match_bytes += length; + ctx->stats.offset_distance += offset; + + // Write initial length bit + length_bit = _log2(--length); + output_bit(ctx, --length_bit < 0); + + // Write offset prefix + if(length == 2 - 1) { + assert(offset <= cfg_short_limit); + offset_prefix = 0; + offset_class = cfg_short_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.short_freq[offset_prefix]; + + // Note that the encoding for short matches is reversed relative to + // the long ones in order to expose holes in the decruncher's offset + // tables + offset_prefix = ~offset_prefix; + } else { + assert(offset <= cfg_long_limit); + offset_prefix = 0; + offset_class = cfg_long_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.long_freq[offset_prefix]; + } + + output_bit(ctx, offset_prefix >> 1); + output_bit(ctx, offset_prefix >> 0); + + // Write offset payload + offset -= offset_class->base; + offset = ~offset; + + offset_bits = offset_class->bits; + while(offset_bits & 7) + output_bit(ctx, offset >> --offset_bits); + + if(offset_bits) + output_literal(ctx, offset); + + // Write any remaining length bits + while(length_bit >= 0) { + output_bit(ctx, length >> length_bit); + output_bit(ctx, --length_bit < 0); + } +} + +static inline void write_output(lz_context *ctx, bool show_trace) { + unsigned cursor; + + bool implicit_match = false; + + unsigned src_end = ctx->src_end; + unsigned read_pos; + unsigned write_pos; + + lz_info *info = ctx->info; + signed length; + + ctx->margin = 0; + for(cursor = ctx->src_begin; cursor < src_end; cursor += length) { + length = info[cursor].match_length; + + if(length > 0) { + unsigned offset; + +# if USE_LITERAL_RUNS + if(!implicit_match) +# endif + { + output_bit(ctx, 0); + } + + offset = info[cursor].match_offset; + encode_match(ctx, offset, length); + + if(show_trace) { + printf ( + "$%04x %smatch(-%u/$%04x, %u bytes)\n", + cursor, + implicit_match ? "" : "explicit-", + offset, + cursor - offset, + length + ); + } + +# if USE_LITERAL_RUNS + implicit_match = false; +# endif + } else { + length = -length; + output_bit(ctx, 1); + +# if USE_LITERAL_RUNS + { + + // Normally a match implicitly follows a literal run except for the + // case of a maximum length literal run, or for when the the streaming + // version crosses into the next page + if(cfg_per_page) + implicit_match = !wraps(cursor, length, RUN_LIMIT); + else + implicit_match = length < RUN_LIMIT; + + // The parser may generate a short run followed by one or more maximum + // length runs for split literals. This needs to be avoided manually + // by reversing the order + if(implicit_match) { + signed next_length = -info[cursor + length].match_length; + if(next_length > 0) { + info[cursor].match_length = -next_length; + info[cursor + next_length].match_length = -length; + length = next_length; + + assert(length == RUN_LIMIT); + implicit_match = false; + } + } + + encode_literals(ctx, cursor, length); + } +# else + output_literal(ctx, ctx->src_data[cursor]); +# endif + + if(show_trace) { + printf ( + "$%04x literal(%u bytes)\n", + cursor, + length + ); + } + } + // Check for overlap between written area and packed data, in case bump safety margin + read_pos = cursor - ctx->src_begin + length; + write_pos = output_bitsize(ctx) / 8 + 1; + if(read_pos > write_pos + ctx->margin) ctx->margin = read_pos - write_pos; + + } + +# if VERIFY_COST_MODEL + { + unsigned expected = info[ctx->src_begin].cumulative_cost; + unsigned actual = output_bitsize(ctx); + + if(expected != actual) { + printf ( + "expected: %u\n" + "actual: %u\n", + expected, + actual + ); + } + } +# endif + + // The sentinel is a maximum-length match +# if USE_LITERAL_RUNS + if(!implicit_match) +# endif + { + output_bit(ctx, 0); + } + + encode_match(ctx, 1, RUN_LIMIT + 1); +} + + +/****************************************************************************** + * Parse out the set of offset bit lengths from a descriptor string + ******************************************************************************/ +static void prepare_offset_lengths(offset_length_t *table, size_t count) { + unsigned base; + unsigned limit = 0; + unsigned previous = 0; + + do { + unsigned int bits = table->bits; + + if(bits <= previous) + fatal("offset lengths must be listed in ascending order"); + previous = bits; + if(bits > OFFSET_LENGTH_LIMIT) + fatal("offset lengths cannot be wider than %u bits", OFFSET_LENGTH_LIMIT); + + base = limit + 1; + limit += 1 << bits; + table->base = base; + table->limit = limit; + ++table; + } while(--count); +} + +static inline bool parse_offset_lengths(const char *text) { + if(sscanf(text, "%u/%u/%u/%u:%u/%u/%u/%u", + &cfg_short_offset[0].bits, &cfg_short_offset[1].bits, + &cfg_short_offset[2].bits, &cfg_short_offset[3].bits, + &cfg_long_offset[0].bits, &cfg_long_offset[1].bits, + &cfg_long_offset[2].bits, &cfg_long_offset[3].bits) != 8) { + return false; + } + prepare_offset_lengths(cfg_short_offset, 4); + prepare_offset_lengths(cfg_long_offset, 4); + return true; +} + + +/****************************************************************************** + * Generate decruncher the tables corresponding to a particular offset length + * sequence. + * These are admittedly rather convoluted and tied tightly to how matches are + * handled in the implementation. See the source for details + ******************************************************************************/ + +static char single_offset ( + const offset_length_t *class, + unsigned shift +) { + signed offset = class->base + 1; + if(class->bits > 8) + offset += 0x8000; + else if(class->bits == 8) + offset += 0x0100; + offset = -offset; + offset >>= shift; + offset &= 0xFF; + + return offset; +} + +static void write_single_offset ( + FILE *file, + const offset_length_t *class, + unsigned shift +) { + char binary[9]; + + signed offset = class->base + 1; + if(class->bits > 8) + offset += 0x8000; + else if(class->bits == 8) + offset += 0x0100; + offset = -offset; + offset >>= shift; + offset &= 0xFF; + + { + char *digit = &binary[8]; + *digit = '\0'; + do { + *--digit = (offset & 1)["01"]; + offset >>= 1; + } while(digit != binary); + } + + fprintf ( + file, + "%s\t\t.byte %%%s\t\t;%u-%u%s\n", + class->bits < shift ? ";" : "", + binary, + class->base, + class->limit, + class->bits < shift ? " (unreferenced)" : "" + ); +} + +static inline void write_offsets(FILE* file) { + static const char length_codes[] = { + 0, + 0xff, + 0x7f, + 0x3f, + 0x1f, + 0x0f, + 0x07, + 0x03, + 0x00, + 0xbf, + 0x5f, + 0x2f, + 0x17, + 0x0b, + 0x05, + 0x02 + }; + + ptrdiff_t i; + + for(i = 0; i <= 3; ++i) { + unsigned bits = cfg_long_offset[i].bits; + fputc(length_codes[bits], file); + } + for(i = 3; i >= 0; --i) { + unsigned bits = cfg_short_offset[i].bits; + fputc(length_codes[bits], file); + } + for(i = 0; i <= 3; ++i) + fputc(single_offset(&cfg_long_offset[i], 0), file); + for(i = 3; i >= 0; --i) + fputc(single_offset(&cfg_short_offset[i], 0), file); + // MSB of base offsets + for(i = 0; i <= 3; ++i) + fputc(single_offset(&cfg_long_offset[i], 8), file); + for(i = 3; i >= 0; --i) + fputc(single_offset(&cfg_short_offset[i], 8), file); +} + +static inline void write_offset_tables(const char *name) { + static const char long_title[] = "\t\t;Long (>2 byte matches)\n"; + static const char short_title[] = "\t\t;Short (2 byte matches)\n"; + + static const char *const length_codes[] = { + NULL, + "11111111", // 1-bits + "01111111", // 2-bits + "00111111", // 3-bits + "00011111", // 4-bits + "00001111", // 5-bits + "00000111", // 6-bits + "00000011", // 7-bits + "00000000", // 8-bits: This needs a bit of special-processing + "10111111", // 9-bits + "01011111", // 10-bits + "00101111", // 11-bits + "00010111", // 12-bits + "00001011", // 13-bits + "00000101", // 14-bits + "00000010" // 15-bits + }; + + ptrdiff_t i; + unsigned near_longs; + + // Open the target file + FILE *file = fopen(name, "w"); + if(!file) + fatal("cannot create '%s'", name); + + // Bit lengths + fprintf(file, "_lz_moff_length\n"); + fprintf(file, long_title); + near_longs = 0; + for(i = 0; i <= 3; ++i) { + unsigned bits = cfg_long_offset[i].bits; + fprintf(file, "\t\t.byte %%%s\t\t;%u bits\n", + length_codes[bits], bits); + if(bits < 8) + ++near_longs; + } + fprintf(file, short_title); + for(i = 3; i >= 0; --i) { + unsigned bits = cfg_short_offset[i].bits; + fprintf(file, "\t\t.byte %%%s\t\t;%u bits\n", + length_codes[bits], bits); + } + // LSB of base offsets + fprintf(file, "_lz_moff_adjust_lo\n"); + fprintf(file, long_title); + for(i = 0; i <= 3; ++i) + write_single_offset(file, &cfg_long_offset[i], 0); + fprintf(file, short_title); + for(i = 3; i >= 0; --i) + write_single_offset(file, &cfg_short_offset[i], 0); + // MSB of base offsets + fprintf(file, "_lz_moff_adjust_hi = *-%u\n", near_longs); + fprintf(file, long_title); + for(i = 0; i <= 3; ++i) + write_single_offset(file, &cfg_long_offset[i], 8); + fprintf(file, short_title); + for(i = 3; i >= 0; --i) + write_single_offset(file, &cfg_short_offset[i], 8); + + fclose(file); +} + + +/****************************************************************************** + * Print some basic statistics about the encoding of the file + ******************************************************************************/ +static inline void print_statistics(const lz_context *ctx, FILE *file) { + unsigned input_size = ctx->src_end - ctx->src_begin; + + fprintf ( + file, + "input file:\t" "%u bytes\n" + "output file:\t" "%u bytes, %u bits (%.2f%% ratio)\n" + "short offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "long offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "%u matches:\t" "%u bytes, %f avg\n" + "%u literals:\t" "%u bytes, %f avg\n" + "avg offset:\t" "%f bytes\n", + + input_size, + ctx->stats.output_size, + ctx->info->cumulative_cost, + 100.0 * ctx->stats.output_size / input_size, + + cfg_short_offset[0].base, + cfg_short_offset[0].limit, + ctx->stats.short_freq[0], + cfg_short_offset[1].base, + cfg_short_offset[1].limit, + ctx->stats.short_freq[1], + cfg_short_offset[2].base, + cfg_short_offset[2].limit, + ctx->stats.short_freq[2], + cfg_short_offset[3].base, + cfg_short_offset[3].limit, + ctx->stats.short_freq[3], + cfg_long_offset[0].base, + cfg_long_offset[0].limit, + ctx->stats.long_freq[0], + cfg_long_offset[1].base, + cfg_long_offset[1].limit, + ctx->stats.long_freq[1], + cfg_long_offset[2].base, + cfg_long_offset[2].limit, + ctx->stats.long_freq[2], + cfg_long_offset[3].base, + cfg_long_offset[3].limit, + ctx->stats.long_freq[3], + + ctx->stats.match_count, + ctx->stats.match_bytes, + (double) ctx->stats.match_bytes / ctx->stats.match_count, + + ctx->stats.literal_runs, + ctx->stats.literal_bytes, + (double) ctx->stats.literal_bytes / ctx->stats.literal_runs, + + (double) ctx->stats.offset_distance / ctx->stats.match_count + ); +} + +/****************************************************************************** + * Helper functions + ******************************************************************************/ +signed read_number(char* arg) { + if(arg[0] == '$') return strtoul(arg + 1, NULL, 16); + else if(arg[0] == '0' && arg[1] == 'x') return strtoul(arg + 2, NULL, 16); + return strtoul(arg, NULL, 10); +} + +unsigned compress(lz_context* ctx, char* output_name, unsigned window) { + unsigned packed_size; + + generate_hash_table(ctx); + find_matches(ctx, window); + output_open(ctx, output_name); + write_output(ctx, 0); + output_close(ctx); + + if(ctx->dst_file = fopen(output_name, "rb+"), !ctx->dst_file) + fatal("error: cannot create '%s'", output_name); + fseek(ctx->dst_file, 0L, SEEK_END); + packed_size = ftell(ctx->dst_file); + fclose(ctx->dst_file); + return packed_size; +} + +/****************************************************************************** + * The main function + ******************************************************************************/ +int +#ifdef _MSC_VER +__cdecl +#endif +main(int argc, char *argv[]) { +#define OUTPUT_NONE 0 +#define OUTPUT_RAW 1 +#define OUTPUT_SFX 2 +#define OUTPUT_LEVEL 3 + + enum { INFINITE_WINDOW = (unsigned) INT_MIN }; + + const char *program_name; + const char *input_name; + char *output_name; + unsigned name_length; + unsigned window; + unsigned cut_origin; + unsigned cut_limit; + const char *emit_offset_tables; + bool show_stats; + bool show_trace; + bool is_cbm = true; + unsigned opt_addr = 0; + unsigned margin; + unsigned packed_size; + unsigned source_size; + char lengths[24]; + unsigned lbits[6], sbits[6]; + unsigned lbest, sbest; + unsigned iterations = 0; + + unsigned decruncher_size = 0; + signed start_addr = -1; + unsigned data_addr = 0; + bool write_tables = false; + + signed output_type = OUTPUT_LEVEL; + + lz_context ctx; + + // Parse the command line + program_name = *argv; + output_name = NULL; + window = INFINITE_WINDOW; + cut_origin = 0; + cut_limit = INT_MAX; + emit_offset_tables = NULL; + show_stats = false; + show_trace = false; + memset(&ctx.stats, 0, sizeof ctx.stats); + parse_offset_lengths(DEFAULT_LENGTHS); + + while(++argv, --argc) { + if(argc >= 2 && !strcmp(*argv, "-o")) { + output_name = *++argv; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--window")) { + window = strtoul(*++argv, NULL, 0); + --argc; + + if(window < RUN_LIMIT || ((window - 1) & window)) { + fatal("window size must be a power of two " + "larger than 0x%x", RUN_LIMIT); + } + + // This implicitly forces paged rendering + cfg_per_page = true; + } else if(argc >= 2 && !strcmp(*argv, "--sfx")) { + start_addr = read_number(*++argv); + output_type = OUTPUT_SFX; + write_tables = true; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--raw")) { + output_type = OUTPUT_RAW; + } else if(argc >= 2 && !strcmp(*argv, "--level")) { + output_type = OUTPUT_LEVEL; + } else if(argc >= 3 && !strcmp(*argv, "--cut-input")) { + cut_origin = read_number(*++argv); + cut_limit = read_number(*++argv); + argc -= 2; + } else if(!strcmp(*argv, "--best-offset-tables")) { + iterations = 4; + } else if(!strcmp(*argv, "--per-page")) { + cfg_per_page = true; + } else if(argc >= 2 && !strcmp(*argv, "--offset-lengths")) { + if(!parse_offset_lengths(*++argv)) + break; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--emit-offset-tables")) { + emit_offset_tables = *++argv; + --argc; + } else if(!strcmp(*argv, "--include-tables")) { + write_tables = true; + } else if(!strcmp(*argv, "--statistics")) { + show_stats = true; + } else if(!strcmp(*argv, "--trace-coding")) { + show_trace = true; + } else if(!strcmp(*argv, "--binfile")) { + is_cbm = false; + } else { + break; + } + } + + if(emit_offset_tables) { + write_offset_tables(emit_offset_tables); + // It allowed to just generate the offset tables + if(!argc) + return EXIT_SUCCESS; + } + + if(argc != 1) { + fprintf ( + stderr, + "syntax: %s\n" + "\t[-o output.lz]\n" + "\t[--window window-size]\n" + "\t[--per-page]\n" + "\t[--cut-input origin size]\n" + "\t[--offset-lengths s1/s2/s3/s4:l1/l2/l3/l4]\n" + "\t[--emit-offset-tables tables.asm]\n" + "\t[--statistics]\n" + "\t[--trace-coding]\n" + "\t[--binfile]\n" + "\t[--best-offset-tables]\n" + "\t[--include-tables]\n" + "\t[--sfx startaddr]\n" + "\t[--level]\n" + "\t[--raw]\n" + "\t{input-file}\n", + program_name + ); + return EXIT_FAILURE; + } + + input_name = *argv; + + if (is_cbm) { + if(window != INFINITE_WINDOW) { + fprintf(stderr, "warning: sliding-window used with " + "a PRG file\n"); + } + } else { + if (output_type == OUTPUT_SFX || output_type == OUTPUT_LEVEL) { + fprintf(stderr, "error: sfx/level not supported with raw binaries\n"); + return EXIT_FAILURE; + } + } + + // If necessary generate output file by substituting the + // extension for .lz + if(!output_name) { + static const char extension[] = ".lz"; + name_length = sizeof input_name + 1; + + output_name = alloca(name_length + sizeof extension); + + memcpy(output_name, input_name, name_length); + memcpy(&output_name[name_length], extension, sizeof extension); + } + + decruncher_size = sizeof decruncher; + + read_input(&ctx, input_name, is_cbm); + cut_input(&ctx, cut_origin, cut_limit); + + if (iterations > 0) { + unsigned temp, j; + setbuf(stdout, NULL); + lbits[0] = sbits[0] = 0; + lbits[1] = sbits[1] = 1; + lbits[2] = sbits[2] = 13; + lbits[3] = sbits[3] = 14; + lbits[4] = sbits[4] = 15; + lbits[5] = sbits[5] = 16; + + packed_size = 0; + while (iterations--) { + for (j = 1; j < 5; j++) { + sbest = 0; + for (sbits[j] = sbits[j-1] + 1; sbits[j] < sbits[j+1]; sbits[j]++) { + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[1],sbits[2],sbits[3],sbits[4],lbits[1],lbits[2],lbits[3],lbits[4]); + parse_offset_lengths(lengths); + temp = compress(&ctx, output_name, window); + if (sbest == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + sbest = sbits[j]; packed_size = temp; + } + } + sbits[j] = sbest; + + lbest = 0; + for (lbits[j] = lbits[j-1] + 1; lbits[j] < lbits[j+1]; lbits[j]++) { + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[1],sbits[2],sbits[3],sbits[4],lbits[1],lbits[2],lbits[3],lbits[4]); + parse_offset_lengths(lengths); + temp = compress(&ctx, output_name, window); + if (lbest == 0 || temp < packed_size) { + printf("best bitlengths: %s \r", lengths); + lbest = lbits[j]; packed_size = temp; + } + } + lbits[j] = lbest; + } + } + + sprintf(lengths,"%u/%u/%u/%u:%u/%u/%u/%u",sbits[1],sbits[2],sbits[3],sbits[4],lbits[1],lbits[2],lbits[3],lbits[4]); + parse_offset_lengths(lengths); + printf("best bitlengths: %s \n", lengths); + } + + // Do the compression + generate_hash_table(&ctx); + find_matches(&ctx, window); + + output_open(&ctx, output_name); + + if(output_type == OUTPUT_LEVEL) { + // Add 2 blank bytes here first, as the address can not be calculated yet + fputc(0, ctx.dst_file); + fputc(0, ctx.dst_file); + } else if(output_type == OUTPUT_SFX) { + // Output decruncher with loadadress 0x801 + fwrite(decruncher, decruncher_size, 1, ctx.dst_file); + } + + if(output_type == OUTPUT_LEVEL) { + // Add depack address + fputc(ctx.src_begin & 0xff, ctx.dst_file); + fputc(ctx.src_begin >> 8, ctx.dst_file); + } + if(output_type == OUTPUT_SFX) { + // Add depack address + fputc(start_addr & 0xff, ctx.dst_file); + fputc(start_addr >> 8, ctx.dst_file); + } + + if(write_tables) write_offsets(ctx.dst_file); + + write_output(&ctx, show_trace); + output_close(&ctx); + + if(ctx.dst_file = fopen(output_name, "rb+"), !ctx.dst_file) + fatal("error: cannot create '%s'", output_name); + fseek(ctx.dst_file, 0L, SEEK_END); + packed_size = ftell(ctx.dst_file); + source_size = ctx.src_end - ctx.src_begin; + margin = ctx.margin + packed_size - source_size; + + if(output_type == OUTPUT_LEVEL) { + // Optimal load address + packed_size -= 2; + } else if(output_type == OUTPUT_SFX) { + packed_size -= decruncher_size; + } + + printf("safety-margin: %d bytes\n", margin); + printf("source size: $%04x (%d)\n", source_size, source_size); + printf("packed data: $%04x (%d)\n", packed_size, packed_size); + + if(output_type == OUTPUT_LEVEL) { + opt_addr = ctx.src_end - packed_size + margin; + printf("source load: $%04x-$%04x\n", ctx.src_begin, ctx.src_end); + printf("packed load: $%04x-$%04x\n", opt_addr, opt_addr + packed_size); + printf("output filetype: level\n"); + } else if(output_type == OUTPUT_SFX) { + printf("start address: $%04x (%d)\n", start_addr, start_addr); + printf("final size: $%04x (%d)\n", packed_size + decruncher_size, packed_size + decruncher_size); + printf("output filetype: sfx\n"); + } else { + printf("output filetype: raw\n"); + } + + if(output_type == OUTPUT_LEVEL) { + // Fix optimal load address to file + fseek(ctx.dst_file, 0, SEEK_SET); + fputc(opt_addr & 0xff, ctx.dst_file); + fputc(opt_addr >> 8, ctx.dst_file); + } else if (output_type == OUTPUT_SFX) { + packed_size -= 26; + data_addr = decruncher_size + packed_size - 0xff + 0x800 + 24; + + // Set up depacker values + fseek(ctx.dst_file, 0x1f, SEEK_SET); + fputc((packed_size >> 8) + 1, ctx.dst_file); + + fseek(ctx.dst_file, 0x22, SEEK_SET); + fputc(data_addr & 0xff, ctx.dst_file); + fputc(data_addr >> 8, ctx.dst_file); + + fseek(ctx.dst_file, 0x34, SEEK_SET); + fputc((0x10000 - packed_size) & 0xff, ctx.dst_file); + + fseek(ctx.dst_file, 0x69, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx.dst_file); + fseek(ctx.dst_file, 0xaa, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx.dst_file); + fseek(ctx.dst_file, 0x106, SEEK_SET); + fputc((0x10000 - packed_size) >> 8, ctx.dst_file); + + fseek(ctx.dst_file, 0x71, SEEK_SET); + fputc(ctx.src_begin & 0xff, ctx.dst_file); + fputc(ctx.src_begin >> 8, ctx.dst_file); + } + fclose(ctx.dst_file); + + // Display some statistics gathered in the process + if(show_stats) + print_statistics(&ctx, stdout); + return EXIT_SUCCESS; +} diff --git a/loader/tools/doynamite1.1/readme.txt b/loader/tools/doynamite1.1/readme.txt new file mode 100644 index 0000000..87d65cc --- /dev/null +++ b/loader/tools/doynamite1.1/readme.txt @@ -0,0 +1,24 @@ +What changed compared to v1.0? +· Literals are now copied forwards and thus the safety margin is low (typically 4 bytes or less) - Needless to say that the format is incompatible with v1.0 +· Selfextracting executables can now be generated by the cruncher +· An optimal loading address can be generated, based on the estimated safety margin +· No more .prg/.bin autodetection. Default input filetype is now .prg, raw binaries can be packed with --binfile +· Iterative mechanism to find the best offset lengths and possibility to add the offset table to the output file +· Simple version in /simple/ generates ~0,5% bigger files but has a $e1 bytes big depacker and depacks around 5-10% faster. This is achieved by rearranging the encoding and forgoing on the offsets base adjustment. + +The file to be compressed is a required argument, in addition the following switches are available: +· -o: Specify the output file name. By default the input file with an .lz extension. +· --window: Specify the window size for with streaming decompression. +· --per-page: Force the windowed encoding for regular files. This is handy when combining both types of data. +· --cut-input: Only specific segment of the file. +· --offset-lengths: Use an alternate set of offset lengths. For e.g. --offset-lengths 3/6/8/10:4/7/10/13 +· --emit-offset-tables: Generate the appropriate decruncher tables for the chosen offset lengths. +· --statistics: Display some basic information about the types of matches made. +· --best-offset-tables: Find the best offset lengths for optimal compression results. This is still slow and done lazy. +· --binfile: Read from a raw binary without a preceeding loadaddress. As a default the cruncher expects a .prg-style file. +· --include-tables: Include the offset tables (24 byte without gaps) to the output file, straight after load- and depack-address. +· --sfx: Spit out a selfextracting .prg +· --level: Spit out a level-packed file, including a generated load- and depack-address (the load-address of the .prg). +· --raw: Spit out the raw pakced data without any additional bytes added before. + +In case the readme.txt of the previous version might be a good read. diff --git a/loader/tools/doynamite1.1/sfx.asm b/loader/tools/doynamite1.1/sfx.asm new file mode 100644 index 0000000..ad1ea21 --- /dev/null +++ b/loader/tools/doynamite1.1/sfx.asm @@ -0,0 +1,232 @@ +!cpu 6510 + +;EXAMPLE FOR OFFICIAL VERSION + +lz_sector = ($ffff - (data_end-data) + 1) & $ff00 + +decruncher = $00c2 + + * = $0801 + ;basicline 1 SYS2061 + !byte $0b,$08,$39,$05,$9e,$32 + !byte $30,$36,$31,$00,$00,$00 + + sei + inc $01 + + ldx #$ff + txs + + inx +- + lda copy_start,x + sta decruncher,x + inx + bne - + + ldy #(>(data_end-data)) + 1 +- + ;src should be data + packed_size + dex +src lda data_end-$100,x +dst sta $ff00,x + txa + bne - + + dec src+2 + dec dst+2 + dey + bne - + + ldx #<($ffff - (data_end-copy_end) + 1) + jmp go + +copy_start +!pseudopc decruncher { + ;fetch depack addr (use --add-depack-addr on lz) +lz_match !byte $00,$00 +lz_bits !byte $00 +lz_scratch !byte $00 + +go + ;******** Start the next match/literal run ******** +lz_decrunch + ;XXX TODO lz_bist auch gleich passend füllen bei sfx, nicht in stream schreiben? + sec ;This is the main entry point. Forcibly +_lz_type_refill jsr _lz_refill_bits ;fill up the the bit buffer on entry + bne _lz_type_cont ;(BRA) + + ;Wrap the high-byte of the destination pointer. +_lz_mfinish bcc *+4 +_lz_maximum inc+1 lz_dst+1 ;This is also used by maximum length + ;literals needing an explicit type bit + + ;Literal or match to follow? + asl lz_bits +_lz_type_cont bcc _lz_do_match + beq _lz_type_refill + + ;******** Process literal run ******** + + lda #%00000000 ;Decode run length +_lz_lrun_loop rol + asl lz_bits + bcs _lz_lrun_test +_lz_lrun_back asl lz_bits + bne _lz_lrun_loop + + jsr _lz_refill_bits + bne _lz_lrun_loop ;(BRA) + +_lz_lrun_test bne _lz_lrun_gotten + jsr _lz_refill_bits + bcc _lz_lrun_back + +_lz_lrun_gotten + sta+1 _lz_copy_cnt+1 ;Store LSB of run-length + ldy #$00 +_lz_lcopy +lz_sector_ptr2 = *+1 ;Copy the literal data. + lda lz_sector,x + inx + bne *+5 + jsr lz_fetch_sector +lz_dst = * + 1 + sta $4000,y + iny +_lz_copy_cnt cpy #$00 + bne _lz_lcopy + + ;Time to advance the destination pointer. + ;Maximum run length literals exit here as a type-bit needs + ;to be fetched afterwards + tya + beq _lz_maximum ;maximum literal run, bump sector pointers and so on + clc + adc+1 lz_dst+0 + sta+1 lz_dst+0 + bcc _lz_do_match + inc+1 lz_dst+1 + + ;******** Process match ******** + +_lz_do_match lda #%00100000 ;Determine offset length by a two-bit +_lz_moff_range asl lz_bits ;prefix combined with the first run + bne *+5 ;length bit (where a one identifies + jsr _lz_refill_bits ;a two-byte match). + rol ;The rest of the length bits will + bcc _lz_moff_range ;then follow *after* the offset data + + tay + lda _lz_moff_length,y + beq _lz_moff_far + +_lz_moff_loop asl lz_bits ;Load partial offset byte + bne *+9 + sty lz_scratch + jsr _lz_refill_bits + ldy lz_scratch + + rol + bcc _lz_moff_loop + + bmi _lz_moff_near + +_lz_moff_far sta lz_scratch ;Save the bits we just read as the + ;high-byte + +lz_sector_ptr3 = *+1 + lda lz_sector,x ;For large offsets we can load the + inx ;low-byte straight from the stream + bne *+5 ;without going throught the shift + jsr lz_fetch_sector ;register + +; sec + adc _lz_moff_adjust_lo,y ;y .. 2 .. 5? ?! necessary with a full lowbyte?!?! + bcs _lz_moff_pageok + dec lz_scratch + sec +_lz_moff_pageok adc+1 lz_dst+0 + sta lz_match+0 + + lda lz_scratch + adc _lz_moff_adjust_hi,y + sec + bcs _lz_moff_join ;(BRA) + +_lz_moff_near +; sec ;Special case handling of <8 bit offsets. + adc _lz_moff_adjust_lo,y;We may can safely ignore the MSB from +; sec ;the base adjustment table as the + adc+1 lz_dst+0 ;maximum base (for a 4/5/6/7 bit + sta lz_match+0 ;length sequence) is 113 + lda #$ff +_lz_moff_join adc+1 lz_dst+1 + sta lz_match+1 + + cpy #$04 ;Get any remaning run length bits + lda #%00000001 + bcs _lz_mrun_start ;Sentinel check can be skipped in that case + +_lz_mrun_loop asl lz_bits + bne *+5 + jsr _lz_refill_bits + rol + asl lz_bits + bcc _lz_mrun_loop + bne _lz_mrun_gotten + jsr _lz_refill_bits + bcc _lz_mrun_loop + + ;XXX TODO only needed on near matches as offset = 1 in that case +_lz_mrun_gotten tay ;A 257-byte (=>$00) run serves as a + beq _lz_end_of_file ;sentinel +_lz_mrun_start + sta _lz_mcopy_len + + ldy #$ff ;The copy loop. This needs to be run + ;forwards since RLE-style matches can overlap the destination +_lz_mcopy + iny + lda (lz_match),y ;Copy one byte + sta (lz_dst),y +_lz_mcopy_len = *+1 + cpy #$ff + bne _lz_mcopy + + tya ;Advance destination pointer +; sec + adc+1 lz_dst+0 + sta+1 lz_dst+0 + jmp _lz_mfinish + +lz_sector_ptr1 = *+1 +_lz_refill_bits ldy lz_sector,x + sty lz_bits +; sec + rol lz_bits + inx + bne + + +lz_fetch_sector + inc lz_sector_ptr1+1 + inc lz_sector_ptr2+1 + inc lz_sector_ptr3+1 ++ + rts + +_lz_end_of_file + + dec $01 + cli + !byte $4c + +_lz_moff_length = * + 2 +_lz_moff_adjust_lo = _lz_moff_length + 8 +_lz_moff_adjust_hi = _lz_moff_length + 16 + +} +copy_end = * + 26 +data +;!bin "d.lz",,2 +data_end diff --git a/loader/tools/doynamite1.1/simple/decrunch.asm b/loader/tools/doynamite1.1/simple/decrunch.asm new file mode 100644 index 0000000..021d80d --- /dev/null +++ b/loader/tools/doynamite1.1/simple/decrunch.asm @@ -0,0 +1,237 @@ +;those will make the depacker ~5% faster but bloat it: +;FAST_LITERAL_COPY = 1 ;will increase size by 11 bytes +;FAST_MATCH_COPY = 1 ;will increase size by 11 bytes + +;------------------------------------------------------------------------------- +;Regular version of the Lempel-Ziv decompressor +;------------------------------------------------------------------------------- +;lz_match = $f9 ;Match source pointer +;lz_dst = $fb ;Decompression destination pointer. +; ;Initialize this to whatever address +; ;you want to decompress to +; +;lz_bits = $fd ;Internal shift register +; +;lz_sector = $0400 ;The one-page buffer from which the +; ;compressed data is actually read, +; ;and which gets refilled by +; ;lz_fetch_sector + + +;------------------------------------------------------------------------------- +;This is the user's hook to replenish the sector buffer with some new bytes. +; +;A and Y are expected to be preserved while carry must remain set on exit. +;X should point to the first byte of the new data, e.g. zero for a full 256-byte +;page of data or two to skip past the sector and track links. +; +;When fetching from a larger in-memory array rather than a single sector buffer +;the lz_sector_ptr1..3 pointers will need to be patched up +;------------------------------------------------------------------------------- +;lz_fetch_sector +; inc lz_sector_ptr1+1 +; inc lz_sector_ptr2+1 +; inc lz_sector_ptr3+1 +; rts + + +;------------------------------------------------------------------------------- +;Typical usage +;------------------------------------------------------------------------------- +; ldx #>source +; ldy # 1 -> short match (len 2) + ;else long match +_lz_do_match + lda #$01 ;this could be made shorter by using the last bitfetch of the upcoming loop and restoring the carry again by a cmp #$02. Saves bytes, but makes things slower, as eof check is also done with all short matches then + asl lz_bits ;first length bit (where a one identifies + bne *+5 ;a two-byte match) + jsr _lz_refill_bits + bcc lz_get_offs ;all done, length is 2, skip further bitfetches +- + asl lz_bits + bne *+5 + jsr _lz_refill_bits + rol + asl lz_bits + bne *+5 + jsr _lz_refill_bits + bcc - + + tay ;A 257-byte (=>$00) run serves as a + beq _lz_end_of_file ;sentinel +lz_get_offs + sta _lz_mcopy_len ;store length at final destination + lda #%11000000 ;fetch 2 more prefix bits + rol ;previous bit is still in carry \o/ +- + asl lz_bits ;XXX TODO in ultra slim variant this could be a subroutine + bne *+5 ;XXX TODO this code could also be called twice + jsr _lz_refill_bits + rol + bcs - + + tay + lda lentab,y + beq lz_far ;XXX TODO currently 8 and 9 bit long offsets carry teh same value here, so if one wants to use both, one has to set the value for 8 bit long offsets + ;to $ff and check things here with a cmp #$ff. Including teh eor #$ff in lz_far or not will then solvet he problem. This is faster though. +- + asl lz_bits + bne *+5 + jsr _lz_refill_bits + rol + bcs - + bmi lz_short +lz_far + eor #$ff ;negate + tay +lz_sector_ptr3 = *+1 + lda lz_sector,x ;For large offsets we can load the + inx ;low-byte straight from the stream + bne lz_join ;without going throught the shift + jsr lz_fetch_sector ;register + !byte $2c ;skip next two bytes +lz_short + ldy #$ff ;XXX TODO GNAAA y is set twice to $ff in the case of short matches +lz_join + adc lz_dst ;subtract offset from lz_dst + sta lz_match + tya ;hibyte + adc lz_dst+1 + sta lz_match+1 + ldy #$ff ;The copy loop. This needs to be run + ;forwards since RLE-style matches can overlap the destination +_lz_mcopy + iny + lda (lz_match),y ;Copy one byte + sta (lz_dst),y +_lz_mcopy_len = *+1 + cpy #$ff + bne _lz_mcopy + + tya ;Advance destination pointer +; sec + adc lz_dst+0 + sta lz_dst+0 + jmp _lz_mfinish + + ;******** Fetch some more bits to work with ******** + +lz_sector_ptr1 = *+1 +_lz_refill_bits ldy lz_sector,x + sty lz_bits +; sec + rol lz_bits + inx + bne + +lz_fetch_sector + inc lz_sector_ptr1+1 + inc lz_sector_ptr2+1 + inc lz_sector_ptr3+1 ++ +_lz_end_of_file rts + +lentab + ;XXX TODO combine tables so that values used twice are eliminated? -> more classes can be used? + ;XXX TODO best rearrange tables by using lookup table? + + ;short offset init values + !byte %11011111 + !byte %11111011 + !byte %00000000 + !byte %10000000 + + ;long offset init values + !byte %11101111 + !byte %11111101 + !byte %10000000 + !byte %11110000 diff --git a/loader/tools/doynamite1.1/simple/lz.c b/loader/tools/doynamite1.1/simple/lz.c new file mode 100644 index 0000000..2f911c3 --- /dev/null +++ b/loader/tools/doynamite1.1/simple/lz.c @@ -0,0 +1,1100 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + false, + true +} bool; + +#define USE_LITERAL_RUNS 1 +#define VERIFY_COST_MODEL 0 +#define DEFAULT_LENGTHS "3/6/8/10:4/7/10/13" + +enum { + RUN_LIMIT = 0x100, + OFFSET_LENGTH_LIMIT = 15 +}; + +// Some definitions for compiler independence +#ifdef _MSC_VER +# include +# include +# define alloca _alloca +# define inline __forceinline +#else +# include +# include +# if defined(__GNUC__) +# define inline __attribute__((always_inline)) +# endif +#endif + +#undef min +#define remainder remainder_ + +// The main crunching structure +typedef struct { + signed short match_length; + unsigned short match_offset; + + union { + signed hash_link; + unsigned cumulative_cost; + }; +} lz_info; + +typedef struct { + unsigned char *src_data; + unsigned src_begin; + unsigned src_end; + signed margin; + + FILE *dst_file; + unsigned dst_bits; + unsigned dst_used; + + lz_info *info; + + signed hash_table[0x100]; + unsigned char dst_literals[RUN_LIMIT * 2]; + + // Some informational counters + struct { + unsigned output_size; + unsigned short_freq[4]; + unsigned long_freq[4]; + unsigned literal_bytes; + unsigned literal_runs; + unsigned match_bytes; + unsigned match_count; + unsigned offset_distance; + } stats; +} lz_context; + +// A bit of global configuration data +typedef struct { + unsigned bits; + unsigned base; + signed limit; +} offset_length_t; + +static offset_length_t cfg_short_offset[4]; +static offset_length_t cfg_long_offset[4]; +#define cfg_short_limit (cfg_short_offset[3].limit) +#define cfg_long_limit (cfg_long_offset[3].limit) + +static bool cfg_per_page = false; + + +/****************************************************************************** + * Various utility functions and bithacks + ******************************************************************************/ +#define countof(n) (sizeof(n) / sizeof *(n)) + +inline unsigned _log2(unsigned value) { +# ifdef __GNUC__ + enum { WORD_BITS = sizeof(unsigned) * CHAR_BIT }; + + return (WORD_BITS - 1) ^ __builtin_clz(value); +# else + signed bits = -1; + + do + ++bits; + while(value >>= 1); + + return bits; +# endif +} + +inline bool wraps(unsigned cursor, unsigned length, unsigned limit) { + return ((cursor + length) ^ cursor) >= limit; +} + +inline unsigned remainder(signed cursor, signed window) { + return -(cursor | -window); +} + +inline unsigned min(unsigned a, unsigned b) { + return (a < b) ? a : b; +} + +#ifdef _MSC_VER +__declspec(noreturn) +#elif defined(__GNUC__) +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +#endif +static void +#ifdef _MSC_VER +__cdecl +#endif +fatal(const char *format, ...) { + va_list args; + + va_start(args, format); + fputs("error: ", stderr); + vfprintf(stderr, format, args); + fputc('\n', stderr); + va_end(args); + + exit(EXIT_FAILURE); +} + + + +/****************************************************************************** + * Manage the output stream + ******************************************************************************/ +inline void _output_doflush(lz_context *ctx) { + putc(ctx->dst_bits, ctx->dst_file); + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +inline void output_open(lz_context *ctx, const char *name) { + if(ctx->dst_file = fopen(name, "wb"), !ctx->dst_file) + fatal("error: cannot create '%s'", name); + + ctx->dst_bits = 1; + ctx->dst_used = 0; +} + +inline void output_close(lz_context *ctx) { + if(ctx->dst_bits != 1) { + while(ctx->dst_bits < 0x100) + ctx->dst_bits <<= 1; + + putc(ctx->dst_bits, ctx->dst_file); + } + + fwrite(ctx->dst_literals, ctx->dst_used, 1, ctx->dst_file); + ctx->stats.output_size = ftell(ctx->dst_file); + fclose(ctx->dst_file); +} + +inline void output_bit(lz_context *ctx, unsigned bit) { + if(ctx->dst_bits >= 0x100) + _output_doflush(ctx); + + ctx->dst_bits <<= 1; + ctx->dst_bits += bit & 1; +} + +inline void output_literal(lz_context *ctx, unsigned value) { + ctx->dst_literals[ctx->dst_used++] = value; +} + +inline unsigned output_bitsize(lz_context *ctx) { + unsigned total; + unsigned bitbuffer; + + total = ftell(ctx->dst_file); + total += ctx->dst_used; + total <<= 3; + + for(bitbuffer = ctx->dst_bits; bitbuffer > 1; bitbuffer >>= 1) + ++total; + + return total; +} + + +/****************************************************************************** + * Read file into memory and allocate per-byte buffers + ******************************************************************************/ +void read_input(lz_context *ctx, const char *name, bool is_cbm) { + FILE *file; + signed length; + unsigned origin; + + if(file = fopen(name, "rb"), !file) + fatal("unable to open '%s'", name); + +# ifdef _MSC_VER + length = _filelength(_fileno(file)); +# else + { + struct stat stat; + stat.st_size = 0; + fstat(fileno(file), &stat); + length = stat.st_size; + } +# endif + + if(length <= 0) + fatal("cannot determine length of '%s'", name); + + { + // Give us a sentinel for the info structure and prevent two-byte + // hashing from overrunning the buffer + unsigned count = length + 1; + + ctx->info = malloc(count * + (sizeof *ctx->info + sizeof *ctx->src_data)); + ctx->src_data = (void *) &ctx->info[count]; + + if(!ctx->info) + fatal("cannot allocate memory buffer"); + + if(fread(ctx->src_data, length, 1, file) != 1) + fatal("cannot read '%s'", name); + } + + // Deal with the PRG file header. We don't write the loading + // address back out to compressed file, however we *do* need to + // consider the decompression address when deciding whether a + // run crosses a page or window boundary + origin = 0; + + if(is_cbm) { + length -= 2; + + if(length < 0) { + fatal("CBM .prg file is too short to " + "fit a 2-byte load address"); + } + + origin = *ctx->src_data++; + origin += *ctx->src_data++ << 8; + } + + ctx->info -= origin; + ctx->src_data -= origin; + ctx->src_begin = origin; + ctx->src_end = origin + length; +} + +// Cut out a specific part of the file to compress +inline void cut_input(lz_context *ctx, unsigned origin, unsigned limit) { + ctx->src_begin += origin; + ctx->src_end = min(ctx->src_end, ctx->src_begin + limit); + if(ctx->src_begin > ctx->src_end) + fatal("no data in address range %d %d", ctx->src_begin, ctx->src_end); +} + + +/****************************************************************************** + * Try to figure out what matches would be the most beneficial + ******************************************************************************/ +inline unsigned costof_run(unsigned run) { + return _log2(run) * 2 + 1; +} + +#if USE_LITERAL_RUNS +inline unsigned costof_literals(unsigned address, unsigned length) { + unsigned cost; + + cost = length * 8; + cost += costof_run(length); + + // Implicit matches cannot be used after the end of a page + // when doing per-page rendering, hence an extra bit is + // needed here + if(cfg_per_page) { + cost += wraps(address, length, RUN_LIMIT); + // A type bit is still always needed after maximum length + // run since another run may follow + } else if(length == RUN_LIMIT) + ++cost; + return cost; +} +#else +enum { COSTOF_LITERAL = 9 }; +#endif + +inline unsigned costof_match(const offset_length_t *class, signed offset, unsigned length) { + unsigned cost = 3; + + while(offset > class->limit) + ++class; + cost += class->bits; + + return cost + costof_run(length - 1); +} + +inline lz_info optimal_parsing_literal(const lz_info *info, unsigned cursor) { + signed length; + unsigned cost; + lz_info result; + +# if USE_LITERAL_RUNS + length = -info[cursor + 1].match_length; + + if(length > 0 && length < RUN_LIMIT) + cost = info[cursor + ++length].cumulative_cost; + else +# endif + { + cost = info[cursor + 1].cumulative_cost; + length = 1; + } + +# if USE_LITERAL_RUNS + cost += costof_literals(cursor, length); +# else + cost += COSTOF_LITERAL; +# endif + + result.match_length = -length; + result.cumulative_cost = cost; + + return result; +} + +inline lz_info optimal_parsing ( + const lz_info *info, + unsigned cursor, + signed match_offset, + unsigned match_length, + unsigned match_limit, + lz_info best_match +) { + unsigned cost; + + if(match_length == 2) { + if(match_offset <= cfg_short_limit) { + cost = costof_match(cfg_short_offset, match_offset, match_length); + goto try_short_match; + } else if(++match_length > match_limit) + return best_match; + } + + do { + cost = costof_match(cfg_long_offset, match_offset, match_length); +try_short_match: + cost += info[cursor + match_length].cumulative_cost; + + if(cost < best_match.cumulative_cost) { + best_match.match_offset = match_offset; + best_match.match_length = match_length; + best_match.cumulative_cost = cost; + } + } while(++match_length <= match_limit); + + return best_match; +} + + + +/****************************************************************************** + * Determine the longest match for every position of the file + ******************************************************************************/ +inline signed *hashof(lz_context *ctx, unsigned a, unsigned b) { + static const unsigned char random[] = { + 0x17, 0x80, 0x95, 0x4f, 0xc7, 0xd1, 0x15, 0x13, + 0x91, 0x57, 0x0f, 0x47, 0xd0, 0x59, 0xab, 0xf0, + 0xa7, 0xf5, 0x36, 0xc0, 0x24, 0x9c, 0xed, 0xfd, + 0xd4, 0xf3, 0x51, 0xb4, 0x8c, 0x97, 0xa3, 0x58, + 0xcb, 0x61, 0x78, 0xb1, 0x3e, 0x7e, 0xfb, 0x41, + 0x39, 0xa6, 0x8e, 0x10, 0xa1, 0xba, 0x62, 0xcd, + 0x94, 0x02, 0x0d, 0x2b, 0xdb, 0xd7, 0x44, 0x16, + 0x29, 0x4d, 0x68, 0x0a, 0x6b, 0x6c, 0xa2, 0xf8, + 0xc8, 0x9f, 0x25, 0xca, 0xbd, 0x4a, 0xc2, 0x35, + 0x53, 0x1c, 0x40, 0x04, 0x76, 0x43, 0xa9, 0xbc, + 0x46, 0xeb, 0x99, 0xe9, 0xf6, 0x5e, 0x8f, 0x8a, + 0xf1, 0x5d, 0x21, 0x33, 0x0b, 0x82, 0xdf, 0x52, + 0xea, 0x27, 0x22, 0x9a, 0x6f, 0xad, 0xe5, 0x83, + 0x11, 0xbe, 0xa4, 0x85, 0x1d, 0xb3, 0x77, 0xf4, + 0xef, 0xb7, 0xf2, 0x03, 0x64, 0x6d, 0x1b, 0xee, + 0x72, 0x08, 0x66, 0xc6, 0xc1, 0x06, 0x56, 0x81, + 0x55, 0x60, 0x70, 0x8d, 0x23, 0xb2, 0x65, 0x5b, + 0xff, 0x4c, 0xb9, 0x7a, 0xd6, 0xe6, 0x19, 0x9b, + 0xb5, 0x49, 0x7d, 0xd8, 0x45, 0x1a, 0x84, 0x32, + 0xdd, 0xbf, 0x9e, 0x2f, 0xd2, 0xec, 0x92, 0x0e, + 0xe8, 0x7c, 0x7f, 0x00, 0x86, 0xde, 0xb6, 0xcf, + 0x05, 0x69, 0xd5, 0x37, 0xe4, 0x30, 0x3c, 0xe1, + 0x4b, 0xaa, 0x3b, 0x2d, 0xda, 0x5c, 0xcc, 0x67, + 0x20, 0xb0, 0x6a, 0x1f, 0xf9, 0x01, 0xac, 0x2e, + 0x71, 0xf7, 0xfc, 0x3f, 0x42, 0xd3, 0xbb, 0xa8, + 0x38, 0xce, 0x12, 0x96, 0xe2, 0x14, 0x87, 0x4e, + 0x63, 0x07, 0xae, 0xdc, 0xa5, 0xc9, 0x0c, 0x90, + 0xe7, 0xd9, 0x09, 0x2a, 0xc4, 0x3d, 0x5a, 0x34, + 0x8b, 0x88, 0x98, 0x48, 0xfa, 0xc3, 0x26, 0x75, + 0xfe, 0xa0, 0x7b, 0x50, 0x2c, 0x89, 0x18, 0x9d, + 0x3a, 0x73, 0x6e, 0x5f, 0xc5, 0xaf, 0xb8, 0x74, + 0x93, 0xe3, 0x79, 0x28, 0xe0, 0x1e, 0x54, 0x31 + }; + + size_t bucket = random[a] ^ b; + return &ctx->hash_table[bucket]; +} + +inline void generate_hash_table(lz_context *ctx) { + unsigned cursor; + + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + for(cursor = 0; cursor < countof(ctx->hash_table); ++cursor) + ctx->hash_table[cursor] = INT_MIN; + + for(cursor = ctx->src_begin; cursor != src_end; ++cursor) { + signed *hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + info[cursor].hash_link = *hash_bucket; + *hash_bucket = cursor; + } +} + +inline void find_matches(lz_context *ctx, unsigned window) { + const unsigned src_begin = ctx->src_begin; + const unsigned src_end = ctx->src_end; + const unsigned char *src_data = ctx->src_data; + lz_info *info = ctx->info; + + unsigned offset_limit = min(window, cfg_long_limit); + unsigned cursor = ctx->src_end; + + info[cursor].cumulative_cost = 0; + + while(cursor != src_begin) { + unsigned match_length; + signed cursor_limit; + unsigned length_limit; + signed *hash_bucket; + signed hash_link; + lz_info best_match; + + --cursor; + + match_length = 1; + cursor_limit = cursor - offset_limit; + + length_limit = RUN_LIMIT; + length_limit = min(length_limit, remainder(cursor, window)); + length_limit = min(length_limit, src_end - cursor); + + hash_bucket = hashof ( + ctx, + src_data[cursor + 0], + src_data[cursor + 1] + ); + + assert((unsigned) *hash_bucket == cursor); + hash_link = info[cursor].hash_link; + *hash_bucket = hash_link; + + best_match = optimal_parsing_literal(info, cursor); + + while(hash_link >= cursor_limit) { + unsigned match_limit = remainder(hash_link, window); + match_limit = min(match_limit, length_limit); + + if(match_length != match_limit) { + unsigned i = match_length + 1; + + if(!memcmp(&src_data[cursor], &src_data[hash_link], i)) { + for(; i != match_limit; ++i) { + if(src_data[cursor + i] != src_data[hash_link + i]) + break; + } + + assert(i <= match_limit); + + best_match = optimal_parsing ( + info, + cursor, + cursor - hash_link, + match_length + 1, + i, + best_match + ); + + match_length = i; + + if(match_length == RUN_LIMIT) + break; + } + } + + hash_link = info[hash_link].hash_link; + } + + info[cursor] = best_match; + } +} + + +/****************************************************************************** + * Write the generated matches and literal runs + ******************************************************************************/ +#if USE_LITERAL_RUNS +inline void encode_literals ( + lz_context *ctx, + unsigned cursor, + unsigned length +) { + signed bit; + const unsigned char *data; + unsigned start = length; + + ctx->stats.literal_bytes += length; + ++ctx->stats.literal_runs; + + bit = _log2(length); + while(--bit >= 0) { + output_bit(ctx, 1); + output_bit(ctx, length >> bit); + } + + output_bit(ctx, 0); + + data = &ctx->src_data[cursor]; + do + output_literal(ctx, data[start - length--]); + while(length); +} +#endif + +inline void encode_match ( + lz_context *ctx, + signed offset, + unsigned length +) { + unsigned offset_bits; + unsigned offset_prefix; + const offset_length_t *offset_class; + signed length_bit; + + ++ctx->stats.match_count; + ctx->stats.match_bytes += length; + ctx->stats.offset_distance += offset; + + // Write length + length_bit = _log2(--length); + output_bit(ctx, --length_bit >= 0); + + while(length_bit >= 0) { + output_bit(ctx, length >> length_bit); + output_bit(ctx, --length_bit < 0); + } + + // Write offset prefix + if(length == 2 - 1) { + assert(offset <= cfg_short_limit); + offset_prefix = 0; + offset_class = cfg_short_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.short_freq[offset_prefix]; + } else { + assert(offset <= cfg_long_limit); + offset_prefix = 0; + offset_class = cfg_long_offset; + + while(offset > offset_class->limit) { + ++offset_class; + ++offset_prefix; + } + + ++ctx->stats.long_freq[offset_prefix]; + } + + output_bit(ctx, offset_prefix >> 1); + output_bit(ctx, offset_prefix >> 0); + + // Write offset payload + offset--; + + offset_bits = offset_class->bits; + if (offset_bits > 7) { + while(offset_bits & 7) + output_bit(ctx, offset >> --offset_bits); + if(offset_bits) + output_literal(ctx, ~offset); + } else { + while(offset_bits & 7) + output_bit(ctx, ~offset >> --offset_bits); + if(offset_bits) + output_literal(ctx, ~offset); + } + +} + +inline void write_output(lz_context *ctx, bool show_trace) { + unsigned cursor; + + bool implicit_match = false; + + unsigned src_end = ctx->src_end; + unsigned read_pos; + unsigned write_pos; + + lz_info *info = ctx->info; + signed length; + + ctx->margin = 0; + for(cursor = ctx->src_begin; cursor < src_end; cursor += length) { + length = info[cursor].match_length; + + if(length > 0) { + unsigned offset; + +# if USE_LITERAL_RUNS + if(!implicit_match) +# endif + { + output_bit(ctx, 0); + } + + offset = info[cursor].match_offset; + encode_match(ctx, offset, length); + + if(show_trace) { + printf ( + "$%04x %smatch($%04x/$%04x, %u bytes)\n", + cursor, + implicit_match ? "" : "explicit-", + offset, + cursor - offset, + length + ); + } + +# if USE_LITERAL_RUNS + implicit_match = false; +# endif + } else { + length = -length; + output_bit(ctx, 1); + +# if USE_LITERAL_RUNS + { + + // Check for overlap between written area and packed data, in case bump safety margin + //read_pos = cursor - ctx->src_begin + length; + //write_pos = output_bitsize(ctx) / 8 + 1; + //if(read_pos > write_pos + ctx->margin) ctx->margin = read_pos - write_pos; + + // Normally a match implicitly follows a literal run except for the + // case of a maximum length literal run, or for when the the streaming + // version crosses into the next page + if(cfg_per_page) + implicit_match = !wraps(cursor, length, RUN_LIMIT); + else + implicit_match = length < RUN_LIMIT; + + // The parser may generate a short run followed by one or more maximum + // length runs for split literals. This needs to be avoided manually + // by reversing the order + if(implicit_match) { + signed next_length = -info[cursor + length].match_length; + if(next_length > 0) { + info[cursor].match_length = -next_length; + info[cursor + next_length].match_length = -length; + length = next_length; + + assert(length == RUN_LIMIT); + implicit_match = false; + } + } + + encode_literals(ctx, cursor, length); + } +# else + output_literal(ctx, ctx->src_data[cursor]); +# endif + + if(show_trace) { + printf ( + "$%04x literal(%u bytes)\n", + cursor, + length + ); + } + } + // Check for overlap between written area and packed data, in case bump safety margin + read_pos = cursor - ctx->src_begin + length; + write_pos = ((output_bitsize(ctx) | 7) + 1) / 8; + if(read_pos > write_pos + ctx->margin) ctx->margin = read_pos - write_pos; + } + +# if VERIFY_COST_MODEL + { + unsigned expected = info[ctx->src_begin].cumulative_cost; + unsigned actual = output_bitsize(ctx); + + if(expected != actual) { + printf ( + "expected: %u\n" + "actual: %u\n", + expected, + actual + ); + } + } +# endif + + // The sentinel is a maximum-length match, without offset +# if USE_LITERAL_RUNS + if(!implicit_match) +# endif + { + output_bit(ctx, 0); + } + + // Write length + signed length_bit = _log2(RUN_LIMIT); + output_bit(ctx, --length_bit >= 0); + + while(length_bit >= 0) { + output_bit(ctx, RUN_LIMIT >> length_bit); + output_bit(ctx, --length_bit < 0); + } + + // Check for overlap between written area and packed data, in case bump safety margin + read_pos = cursor - ctx->src_begin; + write_pos = ((output_bitsize(ctx) | 7) + 1) / 8; + if(read_pos > write_pos + ctx->margin) ctx->margin = read_pos - write_pos; + +} + + +/****************************************************************************** + * Parse out the set of offset bit lengths from a descriptor string + ******************************************************************************/ +static void prepare_offset_lengths(offset_length_t *table, size_t count) { + unsigned limit = 0; + unsigned previous = 0; + + do { + unsigned int bits = table->bits; + + if(bits <= previous) + fatal("offset lengths must be listed in ascending order"); + previous = bits; + if(bits > OFFSET_LENGTH_LIMIT) + fatal("offset lengths cannot be wider than %u bits", OFFSET_LENGTH_LIMIT); + + limit = 1 << bits; + table->base = 0; + table->limit = limit; + ++table; + } while(--count); +} + +inline bool parse_offset_lengths(const char *text) { + if(sscanf(text, "%u/%u/%u/%u:%u/%u/%u/%u", + &cfg_short_offset[0].bits, &cfg_short_offset[1].bits, + &cfg_short_offset[2].bits, &cfg_short_offset[3].bits, + &cfg_long_offset[0].bits, &cfg_long_offset[1].bits, + &cfg_long_offset[2].bits, &cfg_long_offset[3].bits) != 8) { + return false; + } + prepare_offset_lengths(cfg_short_offset, 4); + prepare_offset_lengths(cfg_long_offset, 4); + return true; +} + +inline void write_offsets(FILE* file) { + unsigned bits; + unsigned value; + static const char const length_codes[] = { + 0x00, + 0x7f, + 0xbf, + 0xdf, + 0xef, + 0xf7, + 0xfb, + 0xfd, + 0x00, + 0x00, //XXX TODO can't use value for 9 as beq lz_far would tehn take effect :-( maybe use $ff for 8 and skip eor #$ff? how to test without clobbering carry? all values eor #$ff? -> before eor: beq lz_far? + 0x80, + 0xc0, + 0xe0, + 0xf0, + 0xf8, + 0xfc + }; + + bits = cfg_long_offset[0].bits; + value = length_codes[bits]; + fputc(value, file); + + bits = cfg_short_offset[0].bits; + value = length_codes[bits]; + fputc(value, file); +} + +/****************************************************************************** + * Print some basic statistics about the encoding of the file + ******************************************************************************/ +inline void print_statistics(const lz_context *ctx, FILE *file) { + unsigned input_size = ctx->src_end - ctx->src_begin; + + fprintf ( + file, + "input file:\t" "%u bytes\n" + "output file:\t" "%u bytes, %u bits (%.2f%% ratio)\n" + "short offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "long offsets:\t" "{ %u-%u: %u, %u-%u: %u, %u-%u: %u, %u-%u: %u }\n" + "%u matches:\t" "%u bytes, %f avg\n" + "%u literals:\t" "%u bytes, %f avg\n" + "avg offset:\t" "%f bytes\n", + + input_size, + ctx->stats.output_size, + ctx->info->cumulative_cost, + 100.0 * ctx->stats.output_size / input_size, + + cfg_short_offset[0].base, + cfg_short_offset[0].limit, + ctx->stats.short_freq[0], + cfg_short_offset[1].base, + cfg_short_offset[1].limit, + ctx->stats.short_freq[1], + cfg_short_offset[2].base, + cfg_short_offset[2].limit, + ctx->stats.short_freq[2], + cfg_short_offset[3].base, + cfg_short_offset[3].limit, + ctx->stats.short_freq[3], + cfg_long_offset[0].base, + cfg_long_offset[0].limit, + ctx->stats.long_freq[0], + cfg_long_offset[1].base, + cfg_long_offset[1].limit, + ctx->stats.long_freq[1], + cfg_long_offset[2].base, + cfg_long_offset[2].limit, + ctx->stats.long_freq[2], + cfg_long_offset[3].base, + cfg_long_offset[3].limit, + ctx->stats.long_freq[3], + + ctx->stats.match_count, + ctx->stats.match_bytes, + (double) ctx->stats.match_bytes / ctx->stats.match_count, + + ctx->stats.literal_runs, + ctx->stats.literal_bytes, + (double) ctx->stats.literal_bytes / ctx->stats.literal_runs, + + (double) ctx->stats.offset_distance / ctx->stats.match_count + ); +} + +/****************************************************************************** + * Helper functions + ******************************************************************************/ +signed read_number(char* arg) { + if(arg[0] == '$') return strtoul(arg + 1, NULL, 16); + else if(arg[0] == '0' && arg[1] == 'x') return strtoul(arg + 2, NULL, 16); + return strtoul(arg, NULL, 10); +} + +unsigned compress(lz_context* ctx, char* output_name, unsigned window) { + unsigned packed_size; + + generate_hash_table(ctx); + find_matches(ctx, window); + output_open(ctx, output_name); + write_output(ctx, 0); + output_close(ctx); + + if(ctx->dst_file = fopen(output_name, "rb+"), !ctx->dst_file) + fatal("error: cannot create '%s'", output_name); + fseek(ctx->dst_file, 0L, SEEK_END); + packed_size = ftell(ctx->dst_file); + fclose(ctx->dst_file); + return packed_size; +} + +/****************************************************************************** + * The main function + ******************************************************************************/ +int +#ifdef _MSC_VER +__cdecl +#endif +main(int argc, char *argv[]) { + enum { INFINITE_WINDOW = (unsigned) INT_MIN }; + + const char *program_name; + const char *input_name; + char *output_name; + unsigned name_length; + unsigned window; + unsigned cut_origin; + unsigned cut_limit; + bool show_stats; + bool show_trace; + unsigned i; + bool is_cbm = true; + unsigned opt_addr = 0; + unsigned margin; + unsigned packed_size; + unsigned source_size; + + lz_context ctx; + + // Parse the command line + program_name = *argv; + output_name = NULL; + window = INFINITE_WINDOW; + cut_origin = 0; + cut_limit = INT_MAX; + show_stats = false; + show_trace = false; + memset(&ctx.stats, 0, sizeof ctx.stats); + parse_offset_lengths(DEFAULT_LENGTHS); + + while(++argv, --argc) { + if(argc >= 2 && !strcmp(*argv, "-o")) { + output_name = *++argv; + --argc; + } else if(argc >= 2 && !strcmp(*argv, "--window")) { + window = strtoul(*++argv, NULL, 0); + --argc; + + if(window < RUN_LIMIT || ((window - 1) & window)) { + fatal("window size must be a power of two " + "larger than 0x%x", RUN_LIMIT); + } + + // This implicitly forces paged rendering + cfg_per_page = true; + } else if(argc >= 3 && !strcmp(*argv, "--cut-input")) { + cut_origin = read_number(*++argv); + cut_limit = read_number(*++argv); + //cut_origin = strtoul(*++argv, NULL, 0); + //cut_limit = strtoul(*++argv, NULL, 0); + argc -= 2; + } else if(!strcmp(*argv, "--per-page")) { + cfg_per_page = true; + } else if(argc >= 2 && !strcmp(*argv, "--offset-lengths")) { + if(!parse_offset_lengths(*++argv)) + break; + --argc; + } else if(!strcmp(*argv, "--statistics")) { + show_stats = true; + } else if(!strcmp(*argv, "--trace-coding")) { + show_trace = true; + } else { + break; + } + } + + if(argc != 1) { + fprintf ( + stderr, + "syntax: %s\n" + "\t[-o output.lz]\n" + "\t[--window window-size]\n" + "\t[--per-page]\n" + "\t[--cut-input origin size]\n" + "\t[--offset-lengths s1/s2/s3/s4:l1/l2/l3/l4]\n" + "\t[--statistics]\n" + "\t[--trace-coding]\n" + "\t{input.prg|bin}\n", + program_name + ); + return EXIT_FAILURE; + } + + input_name = *argv; + + // Check extension to figure out whether it's a .PRG file + name_length = 0; + + for(i = 0; input_name[i]; ++i) { + switch(input_name[i]) { + case '/': + case '\\': + case ':': + name_length = 0; + break; + case '.': + name_length = i; + break; + } + } + + if(!name_length) { + name_length = i; + } + if (is_cbm) { + if(window != INFINITE_WINDOW) { + fprintf(stderr, "warning: sliding-window used with " + "a PRG file\n"); + } + } + + // If necessary generate output file by substituting the + // extension for .lz + if(!output_name) { + static const char extension[] = ".lz"; + + output_name = alloca(name_length + sizeof extension); + + memcpy(output_name, input_name, name_length); + memcpy(&output_name[name_length], extension, sizeof extension); + } + + read_input(&ctx, input_name, is_cbm); + cut_input(&ctx, cut_origin, cut_limit); + + // Do the compression + generate_hash_table(&ctx); + find_matches(&ctx, window); + + output_open(&ctx, output_name); + + // Add 2 blank bytes here first, as the address can not be calculated yet + fputc(0, ctx.dst_file); + fputc(0, ctx.dst_file); + // Add the depack address + fputc(ctx.src_begin & 0xff, ctx.dst_file); + fputc(ctx.src_begin >> 8, ctx.dst_file); + + write_output(&ctx, show_trace); + output_close(&ctx); + + if(ctx.dst_file = fopen(output_name, "rb+"), !ctx.dst_file) + fatal("error: cannot create '%s'", output_name); + fseek(ctx.dst_file, 0L, SEEK_END); + packed_size = ftell(ctx.dst_file); + + source_size = ctx.src_end - ctx.src_begin; + margin = ctx.margin + packed_size - source_size; + + if (is_cbm) packed_size -= 2; + + printf("safety-margin: %d bytes\n", margin); + printf("source size: $%04x (%d)\n", source_size, source_size); + printf("packed size: $%04x (%d)\n", packed_size, packed_size); + + opt_addr = ctx.src_end - packed_size + margin; + //printf("optimal load address: $%04x (%d)\n", opt_addr, opt_addr); + printf("source load: $%04x-$%04x\n", ctx.src_begin, ctx.src_end); + printf("packed load: $%04x-$%04x\n", opt_addr, opt_addr + packed_size); + + fseek(ctx.dst_file, 0, SEEK_SET); + fputc(opt_addr & 0xff, ctx.dst_file); + fputc(opt_addr >> 8, ctx.dst_file); + fclose(ctx.dst_file); + + // Display some statistics gathered in the process + if(show_stats) + print_statistics(&ctx, stdout); + return EXIT_SUCCESS; +} diff --git a/loader/tools/doynamite1.1/test.asm b/loader/tools/doynamite1.1/test.asm new file mode 100644 index 0000000..0954106 --- /dev/null +++ b/loader/tools/doynamite1.1/test.asm @@ -0,0 +1,77 @@ +!cpu 6510 + +;EXAMPLE FOR OFFICIAL VERSION + +count = $cffe + +lz_match = $f9 +lz_dst = $fb +lz_bits = $fd + +lz_scratch = $fe + +lz_sector = $0400 + + * = $0900 +!src "decrunch.asm" + + * = $0801 + ;basicline 1 SYS2061 + !byte $0b,$08,$39,$05,$9e,$32 + !byte $30,$36,$31,$00,$00,$00 + + sei + lda #$35 + sta $01 + lda #$00 + sta count + sta count+1 + lda #$01 + sta $d019 + sta $d01a + lda #$7f + sta $dc0d + lda $dc0d + lda $d011 + and #$7f + sta $d011 + lda #$fa + sta $d012 + lda #irq + sta $ffff + cli + jsr go + sei + inc $d020 + jmp * +irq + dec $d019 + inc count + bne + + inc count+1 ++ + rti +go + ldx #data + + jsr lz_decrunch + inc $d020 + sei + jmp * + +*=$4a00 +data + !bin "b.lz",,2 +;*=$6a38 +;data +; !bin "d.lz",,2 +;*=$3804 +;data +; !bin "ras.lz",,2 +;*=$4c74 +;data +; !bin "c.lz",,2 + diff --git a/loader/tools/exomizer-3.1/changelog.txt b/loader/tools/exomizer-3.1/changelog.txt new file mode 100644 index 0000000..0da4c72 --- /dev/null +++ b/loader/tools/exomizer-3.1/changelog.txt @@ -0,0 +1,293 @@ +2020-12-22 +Release of 3.1.0 source code and Win32 binaries. ++ Fixed bug in the sfxdecr.s affecting c128 reported by Fredrik Ramsberg. ++ Implemented split encoding support (-E) in the 6502 decruncher. ++ Updated raw decrunchers for Zilog Z80 contributed by Antonio Villena. ++ Added raw decrunchers for Intel 8080 contributed by Ivan Gorodetsky. ++ Added raw decrunchers for ARM 32bit thumb2 contributed by ALeX Kazik. ++ Implement optional forward decrunching properly in exodecrunch.s and as a + consequence of this also remove krilldecr.s ++ Implement sequence read pointer blacklist to be able to avoid reading from + specified memory areas like hardware registers (Experimental feature), + Requested by Oziphantom. ++ update dasm and add acme exodecrunch sources ++ Improve compression by previous offset reuse, -P-32 to disable ++ Extended -e and -E to be able to reuse/calculate shared header/table info + for multiple crunched files. (Experimental feature, no direct support in + exodecrunch.s yet), Requested by Lazycow + +2019-01-05 +Release of 3.0.2 source code and Win32 binaries. ++ Add documentation about level, mem and raw outfile structure. ++ Fix raw -d -r combination to reverse the inbuffer before decrunch. ++ Fix -P0 literal sequences bug in exodec.c, Bug reported by Nino Porcino. ++ improved cruncher tuning for slightly improved compression on average. ++ Added sfx support for a new target, BBC Micro B (-t 0xbbcb). ++ Added -P+16 awareness to exodecrunch.s ++ Fix bucket optimization to be -T+4 aware in search.c, Bug reported by + Ciccioriccio. ++ Fix absolute offset overflow bug in sfxdecr.s. Bug reported by Comos. + +2018-08-10 +Release of 3.0.1 source code and Win32 binaries. ++ Add missing clc to the new 6502 decrunchers. Bug reported by Soci. + +2018-05-16 +Release of 3.0.0 source code and Win32 binaries. ++ Up to almost 50% faster 6502 decruncher (sfx and stand-alone). However the + bitstream format has changed in an incompatible way. See exo30info.txt for + more info. + +2018-03-08 +Release of 2.0.11 source code and Win32 binaries. ++ No change from preview 3 +2018-03-06 +Release of 2.0.11 preview 3 ++ Improved desfx c64 IO banking handling and automatic decrunched area + detection. ++ sfx: Detected in-file type exported making target auto detection possible for + some sfx bin and sfx (not sfx basic or sfx sys). ++ sfx: Applesingle file write now writes the header descriptors in in ascending + order, suggested by Oliver Schmidt. + +2018-02-20 +Release of 2.0.11 preview 2 ++ Fix compression improvement loop crashing bug exposed by the thread safety + changes for library compilation, reported by Lasse Öörni. ++ Support the AppleSingle format instead of the 4 byte cc65 header, suggested + by Oliver Schmidt. + +2018-02-11 +Release of 2.0.11 preview 1 ++ Add i_raw assembler directive for sfx to generate header less output. ++ Make sfx 0xa2 fall back to 4 byte Apple II cc65 headers for in-files instead + of prg-headers. ++ Add sfx bin directive as a shortcut for creating sfx files with as little + impact on memory outside of the decrunched area as possible. Perfect for + crunching Apple II binary files. + +2017-12-25 +Release of 2.0.10 source code and Win32 binaries. ++ Fix broken things in rawdecrs folder since 2.0.9 ++ Add PET 4032 as sfx target from the nanoflite github fork. + +2017-12-16 +Release of 2.0.10 preview 3 ++ Fix core dump when using max_passes=1 caused by static var removal. ++ Change zp-usage of the sfx-decr for plus4/c16 to avoid overwriting current + device number address. + +2017-12-09 +Release of 2.0.10 preview 2 ++ Add used encoding to the crunch_info struct returned by core crunch func. ++ Rework core compression core to not use local static vars to simplify use + when compiled into a library. ++ rework -C into a generic favor speed flag and make it disable the crunch + result changes too. ++ Improve crunch result slightly by also consider same length sequences at + larger offsets too (but by doing this also slowing it down). + +20170708 +Release of 2.0.10 preview 1 ++ Add a brief output mode enabled with -B, suggested by both Daniel Hotorp and + Bacchus independently. ++ Display progress indication only on ttys and not when output is redirected, + inspired by input from Daniel Hotorp. ++ Make it possible to add offset and length to plain and prg file loading. ++ Improve sfx memory layout dump, suggested by Steffen Görzig. ++ sfx -Di_decr_table=2 should disable i_irq_during, reported by Steffen Görzig. ++ Updated z80 decrunchers, now with License information. ++ More portable by not using negative exit codes. ++ Add new keyword systrim to the sfx command. It behaves like the sys keyword + but will also remove the sysline from the loaded infile. ++ Exit with an error if the parsing of the sfxdecr.s fails. This might happen + with user provided assembly given by the options -x -X -s -f, reported by + Stefan A. Haubenthal. ++ Change -mtune flag to make exomizer build on more platforms "out of the box". + +2015-09-21 +Release of 2.0.9 source code, Win32 and DOS binaries. ++ Fix gcc-compiler warnings. ++ sfx decr src comments echoed to stdout, reported by iAN CooG, fix by soci. ++ NULL pointer dereference crash, reported by Flavio, fix by soci. + +2015-09-20 +Release of 2.0.8 source code, Win32 and DOS binaries. ++ Fix bug reported and analyzed by Adrien Destugues. The ECHO token in asm.y + collides with the flex ECHO macro. The cause is that Bison 2.3a and newer + stopped to generate defines for the declared tokens. To resolve this the ECHO + token has been renamed to ECHO1. ++ Add -E flag to not write the encoding to the outfile. ++ Remove max nr of chunks limit from the chunkpool allocator. ++ Enforce match max_len everwhere, bug reported by Zik / Futurs. + +2013-04-14 +Release of 2.0.7 source code, Win32 and DOS binaries. ++ Bugfixed commodore sfx targets to automatically disable irq when decrunching + over system areas. This together with moving the table to zero-page, + -Di_table_addr=0x2, allows decrunching $0200- without corruption + for all commodore targets except for the vic20-configs without a 3kB memory + expansion since they have a memory hole at $0400-$1000. ++ Bugfixed z80 decrunchers from Metalbrain. ++ Bugfixed sfx c16/plus4 target where the default irq could corrupt memory + while decrunching data that covers $07f6-$0800, reported by Luca/FIRE. ++ Bugfixed sfx c16/plus4 target where the default decrunch effect could corrupt + memory while decrunching data that covers $0be7, reported by Luca/FIRE. ++ Added feature to sfx-mode that complains if the data it too big to fit in the + available memory of the selected target, suggested by Luca/FIRE. ++ Added c16 target, -t 4, like -t4 but with smaller memory, suggested by + Luca/FIRE. + +2013-01-27 +Release of 2.0.6 source code, Win32 and DOS binaries. ++ New improvements to the z80 decrunchers, again smaller and faster. + +2013-01-12 +Release of 2.0.5 source code, Win32 and DOS binaries. ++ Add -C and -M flags that trades crunch result for speed. It is now + possible to really speed up crunching, even for "worst case"-type files. ++ Now skips the DAG traversing of the final pass if the encoding hasn't changed + since the previous pass. + +2012-08-16 +Release of 2.0.4 source code, Win32 and DOS binaries. ++ Bug in z80 decrunchers fixed by Metalbrain. Thanks goes to Hervé Monchatre + and Tim Riemann (Octoate) for reporting. ++ Implement sfx basic for the Apple II target. ++ Improve documentation slightly for the sfx and level commands. + +2012-03-25 +Release of 2.0.3 source code, Win32 and DOS binaries. ++ z80 decrunchers improved by Antonio Villena, now smaller and faster. + +2011-08-19 +Release of 2.0.2 source code, Win32 and DOS binaries. ++ Added 6809-decruncher contributed by Edouard Forler. ++ Fix language errors in the documentation. Thanks to Csabo/LOD. ++ Remove bogus printout about the default decrunch effect when using a custom + decrunch effect. Bug reported by Csabo/LOD. ++ Fix bug that prevented the correct error message from showing when trying to + combine a basic start and a non rom config for the sfx command. Bug reported + by iAN CooG. + +2011-02-06 +Release of 2.0.1 source code, Win32 and DOS binaries. ++ Fix bug in log.c that caused the desfx command to loop if the log level was + lower than debug. Bug report by iAN CooG. +2011-01-22 +Release of 2.0 source code, Win32 and DOS binaries. ++ Fix bug in sfx decruncher setup where data was copied unnecessary. Triggered + by using -Di_load_addr, reported by nbla000 ++ Add i_line_number symbol to be able to change the default line number of the + basic line of the sfx decruncher. Feature suggested by Chicken ++ 6502 emulator core now features memory read/write callbacks ++ Add desfx -e flag to override the automatic entry point detection, feature + suggestion with patch by iAN CooG/HokutoForce. (Patch reworked somewhat, + error handling added) ++ Fix desfx handling of files that decrunch to and including 0xffff, Bug report + with patch by iAN CooG/HokutoForce. (Patch reworked somewhat, end addresses + are exclusive everywhere else) ++ Fix argument handling of desfx, Bug report + patch by iAN CooG/HokutoForce. + +2008-09-08 +Release of 2.0beta7 source code, Win32 and DOS binaries. ++ Refactored some functions from exo_main.c and exo_helper.c into a new file, + exo_util.c so the testprogram could use them too. ++ Fixed a bug in exo_main where the sys/call basic token was mixed up with + the basic_txt_start. This confused thes sfx sys sub-command. ++ Fixed a bug in krilldecr.s. The handling of literal sequences longer than + 256 bytes was broken. ++ Added a testprogram to test that the decrunchers in the exodecrs folder + works. This feature uses the 6502 emulation core to run test programs. It + tests literal sequences longer than 256 bytes and run lengths longer than 256 + bytes for all four decruncher variants, backward, forward, streaming and + chunk streaming. ++ Added a z80 decruncher contributed by Metalbrain. ++ Added an experimental desfx command that should be able to decrunch sfx + crunched files. Uses the 6502 emulation core to run the sfx-decrunchers. ++ Added a 6502 emulator core. ++ Fix rare situation when sfxdecr.s failed to assemble. ++ Improve the help text when no sub-command is given. ++ Improve the handling of ROM/RAM-banking and NMIs for the atari target in the + sfx decruncher. ++ Fix broken op definition ldy abs,x in the sfx assembler, bug reported by + Stefano Tognon. + +2007-05-13 +Prerelease of 2.0beta7 source code, Win32 and DOS binaries. ++ Add loading of Oric tap files to the sfx command. ++ Add support for microcontroller RAM to Oric sfx target. ++ Add flags to customize enter and exit code for the sfx sub-command. ++ The level and mem sub-commands now output more info about the generated file. + +2007-01-28 +Release of 2.0beta6 source code, Win32 and DOS binaries. ++ Fix c128 target bugs in the sfx command ++ Add an experimental Oric 1 target to the sfx command. + +2006-10-08 +Release of 2.0beta5 source code, Win32 and DOS binaries. ++ Add a decruncher able to decrunch forwards, exodepack.s, contributed by + Krill/Plush. ++ Add support for forward crunching to the mem and level sub commands. ++ Remove broken usage of membuf_append that breaks on big-endian targets. Bug + reported by MagerValp. ++ Add a dasm version of the exodecruncher source. + +2006-07-08 +Release of 2.0beta4 source code, Win32 and DOS binaries. ++ Change my email address (again). ++ Added option to the Apple target to disconnect the loaded DOS. ++ Fix length field error in generated Apple II file header. ++ Don't use setjmp/longjmp due to obscure bug when building for mingw with -O3 + -fomit-frame-pointer. Bug reported by iAN CooG/HokutoForce. ++ Add multipass functionality to the assembler in order to be able to do more + complex things in the decruncher source. ++ fix the sfx decruncher to work for a wider range of i_load_addr values. + Bug reported by iAN CooG/HokutoForce. ++ Add optional offset,len for raw command file reading. + +2005-11-14 +Release of 2.0beta3 source code, win32 and dos binaries. ++ i_ram_exit bug for the C64 target found by iAN CooG/HokutoForce fixed. ++ Effect shorthands -n for no effect, -x1 for Accumulator based border flash, + -x2 for X-register based border flash and -x3 for Y-register based border + flash. -x -X ++ removed the i_fast_effect symbol. Now border effects are fast and blinking + char is slow. ++ Added flag -x to the sfx command. ++ Improved docs about the mem command and assembler symbols. ++ fixed spelling bug in i_effect=2 mode. Bug found by iAN CooG/HokutoForce. + +2005-11-02 +Release of 2.0beta2 win32 and dos binaries. ++ added results for canterbury and calgary corpae. ++ beginning a documentation section in exo20info.txt ++ changed the exoraw binary into a sub command of exomizer. ++ renamed symbols i_(ram|irq)_on_(entering|exit) to i_(ram|irq)_(enter|exit) ++ renamed symboli_config_effect to i_effect ++ added symbols i_(ram|irq)_during. ++ added new sfx target -t 168 ($a8), The Atari 400/800 XL/XE computer family. + The sfx file is written as a xex-file. ++ added ability to autodetect and read Atari Xex-files. ++ cmpression results should be exact as 1.1.5 now (if the -c flag is used). + +2005-04-10 +replaced 2.0beta1 ++ tuned the cruncher for better compression, should be on pair with 1.1.5 now. ++ modified the usage of the symbol i_ram_on_exit ++ documented symbol i_table_addr ++ added symbol i_irq_on_exit + +2005-04-06 +replaced 2.0beta1 ++ fixed +4 bank optmization in sfx decruncher ++ fixed bug in non-literal-sequence sfx decruncher that caused + the decrunched data to be offset in memory. + +2005-04-03 +replaced 2.0beta1 ++ fixed -Di_config_effect + +2005-04-02 +initial release of 2.0beta1 no source. diff --git a/loader/tools/exomizer-3.1/exo20info.txt b/loader/tools/exomizer-3.1/exo20info.txt new file mode 100644 index 0000000..b253566 --- /dev/null +++ b/loader/tools/exomizer-3.1/exo20info.txt @@ -0,0 +1,674 @@ +Exomizer 2.0 + +FEATURES + +o sfx mode: + 1) Several different decrunch effects. + 2) Complete user control over memory and IRQ configuration. + 3) Can start BASIC programs properly with built in trampoline by using the + command 'sfx basic' for the Commodore, Apple II and Oric targets. + 3) The decruncher code is assembled on the fly for maximum flexibility by an + embedded assembler. + 4) Will adapt the decruncher to include only features used by the compressed + stream to keep the size down. + 5) Supports the c64, c128 and the vic20 (in several configurations). + 6) The Atari 400/800 XL/XE family is supported as sfx target 168. + 7) Can generate relocated sfx targets without BASIC line. + 8) The Apple ][+ (and //e) is supported as sfx target 162. + 9) The Oric 8-bit family is supported as sfx target 1. + 10) The Commodore PET 4032 is supported as sfx target 4032. + +o Reads Atari xex-files and auto detects the RUNAD vector if the sys keyword + is given to the sfx command. + +o Can load plain files using the @
syntax. + +o Has a literal sequence escape mechanism that detects and handles sequences + of uncrunchable data properly. + +o The level mode of exomizer crunches each in-file separately and appends + them to the out-file. + +o raw mode: + 1) Crunches plain files instead of .prg files. + 2) Crunches forward or backwards instead of just backwards. + 3) Also decrunches. + 4) Handles files larger than 64k. The Canterbury and Calgary corpora + is now possible to crunch. + 5) Comes with two different C-source decrunchers, one thread safe forward + pull decruncher and one low-level backwards push decruncher, perfect to + base an assembler port on. + +o desfx mode that features decrunching of sfx crunched files. + +o Contributed decruncher source (only 2.0 compatible so far) + 1) z80 decrunchers by Metalbrain, Antonio Villena. (rawdecrs/z80/*) + 2) 6809 decruncher by Edouard Forler. (rawdecrs/6809/*) + +------------------------------------------------------------------------------- +-- A beginning of something like documentation -------------------------------- +------------------------------------------------------------------------------- +A note about addresses: +An address in this context is a positive 16-bit value [0 - 65536]. It can be +given in decimal, hexadecimal (prefixed with $, & or 0x) or octal (prefixed with +0) notation. Examples: 3, 54, $3456, 0x1111, 06543. + +Please be aware that in some command line shells $ and & have special meaning +if used unescaped. If you use $ or & as hexadecimal prefix and see strange +behaviour from exomizer you might be bitten by this. Try 0x instead and see if +things improve. + +All addresses that end an interval of any kind, printed by exomizer or given by +the user as an argument, are exclusive. In other words they point to the +address just following the last byte of the memory area the interval covers. + +Exomizer has four sub commands: mem, level, sfx and raw. They all compress +data. They share the following option flags: +---------------- +-c This flag enables 1.x compatibility mode. It disables the use of the + literal sequence escape mechanism. It will make the output files + compatible with Exomizer v1.x. + +-C This flag increases crunching speed and as a side effect also reduces + the crunch result. This flag is for the impatient developer. + +-e |@ + Uses the given encoding parameters for crunching instead of trying to + calculate an optimal set. The encoding must be in the following format: + xxxxxxxxxxxxxxxx,xxxx,xxxxxxxxxxxxxxxx,xxxxxxxxxxxxxxxx + where each x must be a hexadecimal character, 0-9 or A-F. + The Exomizer prints the encoding parameters used when writing the + output file in this format in order to allow for easy copy and paste. + Please note that this option sets the initial encoding parameters and + not the encoding parameters used in the output file. To set the + encoding parameters of the output file this option must be used in + combination with the -p 1 option. + If the encoding starts with a '@' then the rest will be used as a file + name to read the encoding from. It can be used to read encoding files + written by the -E flag described below. Please note that the -P and -T + flags must be the same for this to work since the format is different + for different flag combinations. + +-E This flag enables an experimental feature for encoding reuse between + multiple files. Please note that there is no direct support for + decrunching these files yet apart from the raw -d command and in the + 6502 decruncher exodecrs/exodecrunch.s (when enabled). It changes the + behavior of exomizer in the following ways: + 1) It won't include the encoding in the compressed files. + 2) The compressed files will be named as the outfile (-o) with an + appended number. ".00" for the first file, ".01" for the second and + so on. + 3) The outfile (-o) will be used to write a file containing only the + encoding. + 4) If more than one in-file is given they will all be compressed using + The same encoding which will be optimal for those files in + combination. + 5) When used with -E, the mem subcommand limits the -l setting to only + accept the value "none". It will also change the default from "auto" + to "none". + +-m + Limits the maximum sequence offset used by the cruncher to . + The default limit is an offset of 65535 bytes. A smaller offset + increases the crunching speed but reduces the crunch result. If a + circular buffer decruncher is used it is important that the maximum + offset doesn't exceed the buffer size. + +-M + Limits the maximum sequence length used by the cruncher to . + The default limit is an length of 65535 bytes. A smaller length + increases the crunching speed but reduces the crunch result. + +-o + Sets out-file name. + +-p + Limits the maximum number of optimization passes the cruncher uses when + calculating the encoding parameters. default is 65535. However the + cruncher will always stop when no further improvement to the crunch + result is made. + +-T + Bit-field that deactivates bit stream traits. Valid values are 0 - 7. + 0 bit: Sequences with length 1, 0=use (default), 1=deactivate + 1 bit: Sequences with lengths where the low byte is 1, 2 or 3 and the + high byte is > 0, 0=use (default), 1=deactivate + 2 bit: Literal sequences, same as -c, 0=use (default), 1=deactivate + +-P + Bit-field that disables bit stream protocol modifications. A value of + 0 makes the bit stream compatible with exomizer 2.0. You can prefix the + value with a + or - to set/clear the bits the value represents. Valid + values are 0 - 63. + 0 bit: Big endian bit orientation, 1=enable (default), 0=disable + 1 bit: Copy byte when reading > 7 bits, 1=enable (default), 0=disable + 2 bit: Implicit first literal byte, 1=enable (default), 0=disable + 3 bit: Align bit stream to stream start, 0=disable (default), 1=enable + 4 bit: Four offset decoding tables, 0=disable (default), 1=enable + +-q Enables quiet mode which will disable display output. + +-V Enables brief mode which will disable most display output. + +-v Displays Exomizer version, email address for support and the usage + license. + +-- Will make all arguments that follow be treated as non-options, even if + they begins with a - character. + +-? Displays a help screen that briefly describes the command line syntax + and the valid options. Also for the sub command if one is given. + +------------------------------------------------------------------------------- +-- Information relevant for the mem, sfx and level sub commands --------------- +------------------------------------------------------------------------------- + +The mem, level and sfx command all crunches files backwards. The files are +loaded into a 16-bit address space and are also limited in size to that 16-bit +address space. The format of the input files can be prg files, Atari xex +files, Oric tap files, AppleSingle files, BBC Micro Standard Archive Format +file tuples or located plain files (plain files that are given an address to +load to.) Please note that all input file formats is valid for all sfx targets +which can be a bit unexpected. + + Plain raw files are loaded by adding an address to it using the @-character. +It is also possible to specify an optional offset and optional length if only a +part of the file is to be loaded. Like addresses, the offsets and the lengths +can be given in either decimal or hexadecimal notation using either 0x or $ +prefixes. The offset can also be negative and will then be applied from the end +of the file instead of from the start. + +@[,[][,[]]] + +* To load a raw file to 0x3456, append @0x3456 to the file name. +* To load a raw file to 0x0400 and skip the 16 first bytes and read the + following 37 bytes, append @0x400,16,37 to the file name. +* To load the first 256 bytes of a raw file to 0x0900, append @$900,,256 or + @$900,0,256 to the file name. + + To load a prg file to the address contained in its prg-header you simply add +the file name to the command line. You can append an optional alternative load +address to the file name to override the load address in the header. And like +for the raw files it is also possible to specify an optional offset to start +from in the file and a length. + +[,[][,[][,[]]]] + +Examples: +* To load a prg file to 0x3456, append ,0x3456 to the file name. +* To load a prg file to its default address and skip the 16 first bytes, append + ,,16 to the file name. +* To load the last 254 bytes of a file to 0x3450, append ,0x3450,-0xfe to the + file name. + + Relocation does not work for xex or tap files. A relocated xex or tap file +will simply be treated as if it was a relocated prg file. + + When given multiple input files the sfx and mem commands loads all files to +where they are located and then crunch the covered memory area. Any unused +space between files will be zero-filled. Data segments in xex-files are loaded +sequentially in the same way. INITAD segments in xex-files are ignored. + + The level command, on the other hand, loads each input file separately, +crunches it and then appends the crunched data to the output file. + + The auto detection of xex or Oric tap files is not perfect. Prg files that +load at $ffff or $1616 will be wrongly detected as xex or Oric tap. To disable +the auto detection relocate the prg files to the same address they normally +load to. + +------------------------------------------------------------------------------- +-- The mem sub command -------------------------------------------------------- +------------------------------------------------------------------------------- + The mem command generated files that are used for decrunching from memory. +Normally these files are linked into the program images either by machine code +monitors or by assembler directives like incbin. + + The mem command writes its output in prg format. It has the following option: + +-l
|none + Sets the load address of the output prg file to
. If the + string none is given the output will be a plain file. Defaults to the + address that gives the crunched data as much overlap with the + decrunched data as possible but still allow for in-place decrunching. + However, the file doesn't need to be decrunched from that address. Any + address that doesn't cause data being decrunched to overwrite data yet + to be decrunched will work. +-f Crunch the file forward instead of backwards that is the default. This + means that in this mode the read and write pointers move forward in + memory while decrunching. The exodecrunch.s file supports optional + forward decrunching but defaults to backwards. Read the comments in the + file for more details on the available options. + +------------------------------------------------------------------------------- +-- The level sub command ------------------------------------------------------ +------------------------------------------------------------------------------- + + The level command writes its output file so that the crunched bytes are +returned in correct order for decrunching in the fly while streaming in the +bytes from secondary storage. + +-f Crunch the file forward instead of backwards that is the default. This + means that in this mode the write pointer move forward in memory while + decrunching. The exodecrunch.s does not support forward decrunching. + For this feature to work You'll have to use Krill's contributed + decruncher. + +------------------------------------------------------------------------------- +-- The sfx sub command -------------------------------------------------------- +------------------------------------------------------------------------------- + + The sfx command generates a runnable stand alone in memory decrunching file. +Its first argument must always be the run address. It may be given as an +address or of the following string sys, systrim, bin and basic. + + If the run address is an actual address then it will be used as the target +address of a jmp instruction executed after the decrunching. + + If the run address is the string sys then the run address will be +auto detected by scanning the area of the basic start for a SYS (or CALL) BASIC +command. + + The keyword systrim behaves just like keyword sys but it also excludes the +memory area that contains the SYS BASIC line from being included in the +crunched file. This is very handy for the unexpanded VIC-20 target which +normally can't decrunch sfx-ed programs residing at the BASIC start since it +has a memory hole that will overlap with the crunched data. By using systrim, +the start of decrunched data will be moved a few bytes and by that also move +the crunched data out of the memory hole. + + If the run address is the string bin then the run address will be detected +from the in-file. If no in-file is given that contains any run address then it +will be set to the start address of the combined memory area of the loaded in- +files. An implicit -Di_load_addr= will be used as well so the load and run +addresses of the generated file will be A too. The generated file will not +contain any basic stub. + + If the run address is the string basic then the computer will run a BASIC +program after decrunch. This is not yet implemented for the Atari target. +The Oric target support BASIC start for the Oric 1 and Atmos computers but not +for the Telestrat. + + The run BASIC mode takes up to three optional addresses. They are in order: +start of BASIC program, end of BASIC program and highest address used by +BASIC. + +$ exomizer sfx basic[,[,[,]]] + + The start of BASIC, , is the address where the BASIC program starts in +memory. For many but not all targets it defaults to $0801. + + The end of BASIC, , is the address where the BASIC variables starts in +memory. It defaults to the address where the BASIC program ends. + + The highest address used by BASIC, , is the address where the BASIC +variables end. Its default value depends on the target. + + The BASIC interpreter will write all variables, strings and arrays between the + address and the address. + + Normally these addresses are auto detected and/or defaulted. However, if non +standard settings are needed they can be changed. Examples of such situations +could be when a BASIC program needs to be run at a non standard address and/or +some graphic needs to be protected from BASIC variables. + + The output of the sfx command is a runnable program file that decrunches +itself in memory when run. If an explicit run address or the string bin is +given then some targets like Atari 8-bit, Oric 1, BBC Micro B and Apple II will +be auto detected based on the format of the in files. For other situations or +if auto detection fails then the target defaults to C64 unless the -t +option is given. This option can also be used to override the target auto +detection if necessary. + +The output file format depends on the target used: + + For all Commodore machines the output is a runnable prg-file that starts +with a BASIC line. + + For the Atari target the output is an auto running xex-file. + + For the Apple target the output is an AppleSingle file that almost always +is a PRODOS bin file (file-type 6). But if the loaded file was a AppleSingle +system file then it will be a system file (file-type $ff). However if the run +address is the string basic, then it will be a PRODOS Applesoft basic file. +(file-type $fc) + + For the BBC target the output is a BBC Micro Standard Archive Format file +tuple. (http://archive.retro-kit.co.uk/bbc.nvg.org/std-format.php3.html) + +For the Oric family the output is a Oric tap file. + + Please note that the sfx command doesn't support extended memory since it +can't decrunch into banked memory. + + The sfx command has the following options: + +-t + Sets the decruncher target. The possible targets are: + 1: Oric 1, Oric Atmos and Telestrat + 20: Commodore Vic20, unexpanded memory + 23: Commodore Vic20, 3kB memory expansion + 52: Commodore Vic20, 8 - 32kB memory expansion + 55: Commodore Vic20 8 - 32kB + 3kB memory expansion + 16: Commodore c16 + 4: Commodore plus4 + 64: Commodore c64 (The default target) + 128: Commodore c128 + 4032: Commodore PET 4032 + 0xa2: Apple ][+ and //e + 0xa8: Atari 400/800 XL/XE + 0xbbcb: BBC Micro B + +-X +-x[1-3]| + Sets the decrunch effect. If an assembler fragment is given, it must + not change the X-register, Y-register or modify the carry flag. + Separate instructions with a space. Please note that including spaces + in the fragment requires quoting to work. An example of a slow C64 + x-scroll-register effect: -X "txa and #07 ora #$c8 sta $d016" + Use a value of 1 - 3 to pick a preset effect: + 1: Accumulator based fast border flash + 2: X-register based fast border flash + 3: Y-register based fast border flash + Default effect is a slow character flash. The 1-3 presets is only + available for targets that supports changing border color. + +-n No decrunch effect. This option can't be combined with -X or -x + +-D= + Defines symbols for the sfx assembler. More advanced features of the + decruncher is controlled by defining symbols. More on this in the + decruncher symbol reference section below. + +-s + Specifies an optional assembler fragment to be executed when the + decruncher starts decrunching. It must not change the Y-register. This + assembler fragment is intended for things like clearing the screen, + changing background and/or border color, changing a c128 to 2MHz mode + or enable 'Skip internal cycles' and burst mode on a DTV2. The fragment + syntax is the same as for the -X and -x options. +-f + Specifies an optional assembler fragment to be executed when the + decruncher has finished decrunching. This assembler fragment is + intended for things like restoring a c128 to 1MHz or disabling 'Skip + internal cycles' and burst mode on a DTV2. The fragment syntax is the + same as for the -X and -x options. + +------------------------------------------------------------------------------- +-- The desfx sub command ------------------------------------------------------ +------------------------------------------------------------------------------- + + The desfx command decrunches a file generated by the sfx command and writes it +as a prg-file. It auto detects the entry point from the in-file and runs the +embedded decruncher in order to get to the decrunched data. It auto detects the +decrunched area to be compatible with more versions of exomized sfx files but +might fail if the decrunched area is unusual. + + The desfx command has the following options: + +-e
|load + Overrides the entry point of the decruncher with
or if the + string load is given, the load address of the input file will be used. + +------------------------------------------------------------------------------- +-- The raw sub command -------------------------------------------------------- +------------------------------------------------------------------------------- + + Now let's go back to the command that handles plain files, raw. +The raw command can both crunch and decrunch both forward and backward. It also +handles generic files without any of the above mentioned restrictions. There's +no hard coded limit in file size but it gets increasingly slow with larger +files so there's a practical limit of about 1MB. The output file format of this +command is a plain file. + +It is possible to give an optional offset and length for the in-file like this: +[,offset[,length]]. Negative offsets and lengths are allowed. They +will wrap around to the end of the file. + +To skip the first two bytes but read all the rest of a file do like this: +$ exomizer raw ,2 -o + +To skip the first two bytes and the last two bytes of a file do like this: +$ exomizer raw ,2,-2 -o + +To read 10 bytes at offset 13 from the end of the file do like this: +$ exomizer raw ,-13,10 -o + +------------------------------------------------------------------------------- +-- The included decruncher source code with examples -------------------------- +------------------------------------------------------------------------------- + +There are two directories with source code in the distribution: + +exodecrs/ + This contains the source code of four 6502 assembler decrunchers. One + backwards decruncher that decrunches data crunched by the mem sub + command. It also contains a forward decruncher contributed by + Krill/Plush for data crunched by the mem sub command and the -f option. + Call these two decrunchers once to decrunch the data to its targeted + location. + Also contained in this directory are two streaming forward decrunchers + for data crunched with the raw sub command. Both use a circular buffer + and has a get_byte like interface that are called repeatedly to get one + byte or a block of decrunched data until the end of data is signaled + by the call returning with the carry flag set. However they are not + capable of handling the literal sequence escape mechanism so data must + be cruncher with the -c flag. + Also included are a Makefile and some example code and data that use + the four decrunchers. The assembler syntax is ca65, the assembler of + the cc65 tool chain. This tool chain is needed to build the examples. + +rawdecrs/ + This directory contains two decrunches written in C. One backwards + decruncher with a call once and all data are decrunched interface. + And one forward streaming decruncher with a get_decrunched_byte + interface. Also included are example code using the decrunchers and + a Makefile that can be used for building them. An ANSI-C compiler are + necessary for this. + +All of the included makefiles have only been tested with Gnu make. + +------------------------------------------------------------------------------- +-- Credits for Exomizer 2.0 --------------------------------------------------- +------------------------------------------------------------------------------- + +Feature suggestions +------------------- +Csabo / LOD +Ninja / The Dreams +Richard / TND +Jack Alien / Remember +Krill / Plush + +Contributed code +---------------- +Krill / Plush +Metalbrain + +Bug reports +----------- +iAN CooG / HokutoForce + +Regression testing +------------------ +Ninja / The Dreams +Csabo / LOD +iAN CooG / HokutoForce +Lasse Öörni + +Inspiration +----------- +PuCrunch +The Oneway crunchers, packers and linkers, Byte Boiler, AB, cruel etc. +The Final Super-Compressor by Meidinger & Borris +Timecruncher 3.1 by Matcham of Network + +------------------------------------------------------------------------------- +-- Credits for Exomizer 1.x --------------------------------------------------- +------------------------------------------------------------------------------- +Inspiration +----------- + The Oneway crunchers: Byteboiler, The Cruncher AB and CruelCrunch. + Pasi Ojala's PuCrunch + +Beta testing +------------ + Lasse Öörni + Csabo / LOD + Overkiller / Hokuto Force / PoL + iAN CooG / Hokuto Force + Rafal Szyja + bubis / Resource + Ninja / The Dreams + +Bug Reporting +------------- + Csabo / LOD + Soci / Singular + 6R6 / Nostalgia + Ninja / The Dreams + +Feedback/Suggestions +-------------------- + Count Zer0 + bubis / Resource + Ninja / The Dreams + +------------------------------------------------------------------------------- +-- Contact information -------------------------------------------------------- +------------------------------------------------------------------------------- + + Please don't hesitate to contact me if you have any feature/improvement +suggestions, bugs to report or perhaps just have something to say about the +exomizer program. + +My name is Magnus Lind and my email address is magli143@gmail.com + +------------------------------------------------------------------------------- +-- Reference for the sfx decruncher symbols ----------------------------------- +------------------------------------------------------------------------------- + +The sfx decruncher is tweaked by defining optional symbols to the assembler on +the command line using the -D= flag: + +The following symbol controls if headers are written to the sfx file: + i_raw + + If it is set to anything but 0 then no headers will be written to the sfx file +regardless of what the documentation of other optional symbols say. + +The following symbol controls the sfx load address: + i_load_addr + + If it is unset then the sfx file will load to the default BASIC start address +of the target machine. For Atari the default load address is $2000. For the +Oric the default load address is $0500. + + If it is set then the sfx file will load to the given address and it will not +have any BASIC line. (The Atari target never has a BASIC line). The run address +is the same as the load address. + +The following symbol controls the sfx BASIC line number: + i_line_number + +If unset it will default to 20. This symbol can't be combined with i_load_addr. + +The following symbols control the memory configuration: + i_ram_enter (defaults to standard ROM configuration) + i_ram_during + i_ram_exit (defaults to standard ROM configuration) + +The i_ram_enter symbol differs from the other two. It isn't used to set the +memory configuration. It tells the exomizer of which memory configuration to +expect when the decruncher is run. This information is used to minimize the +decruncher size. + +For the c64 and the c128 target the value of the above symbols will be stored +into $ff00 (c128) or $01 (c64) to set the mem configuration. For the c16/+4 +target the possible values are 0=ROM and 1=RAM. For the Atari the value will be +stored in $d301. The Oric target supports 0=ROM and 1=RAM where 1 will enable +decrunching into the top 16kB RAM supplied by the microdisc controller. + +Default is $37 for the c64, $00 for the c128, 0 for the c16/+4 and Oric and $ff +for the Atari. + +The following symbols control IRQs. (0=disabled,SEI, 1=enabled,CLI) + i_irq_enter (defaults to 1) + i_irq_during + i_irq_exit + +The following symbols control NMIs, this is only implemented for the Atari +target where the value will be stored in $d40e + i_nmi_enter (defaults to $40) + i_nmi_during + i_nmi_exit + +To locate the decrunch table elsewhere than the default tape buffer or $0600 +for The Atari target, use the symbol i_table_addr: + -Di_table_addr=
+ +The Apple ][+ target has an option to disconnect DOS. To do this, set the +i_a2_disable_dos symbol to any value: + -Di_a2_disable_dos=1 + +The Apple ][+ target writes files in the AppleSingle format. This option +overrides the PRODOS filetype of the target file to the set value. To force a +file of system type set the i_a2_file_type symbol to the file type value. +See http://www.easy68k.com/paulrsm/6502/PDOS8TRM.HTM#B.4.2.4 for a list of +valid file types: + -Di_a2_file_type= + +Decruncher effects are not selected by the use of symbols. They are chosen by +the -x or -X flags or deactivated by -n. + +------------------------------------------------------------------------------- +-- Comparison of outfile structure of the level, mem and raw sub commands -- +------------------------------------------------------------------------------- + The crunched files generated by the mem and level sub command are structurally +identical to the files generated by the raw sub command except that they also +contains the decrunch address appended or prepended. For mem there is also the +prg header . The following table shows how the output of the mem and +level sub commands are related to the output of the raw sub command. + + [exomizer mem -lnone] == [exomizer raw -b] + <--decr le> + [exomizer mem -f -lnone] == + [exomizer raw] + [exomizer mem] == + [exomizer raw -b] + <--decr le> + [exomizer mem -f] == + + [exomizer raw] + [exomizer level] == <--decr be> + [exomizer raw -b -r] + [exomizer level -f] == + [exomizer raw] + + is the decrunch pointer, a 16 bit address written in either big +endian or little endian. + + For lines that contans the raw sub command with the -b flag the decruncher +writes backwards in memory and will predecrement (--) the decrunch address. The +value of the address in the file will be the address that immediately follows +the last byte of the uncrunched data. + + For lines that contains the raw sub command without the -b flag the decruncher +writes forward in memory and will postincrement (++) the decrunch address. The +value of the address in the file will be the address of the first byte of the +uncrunched data. + +This also means that it is possible to decrunch level and mem files using the +raw sub command if you use the offset and len options to skip the extra +addresses. Please note that the resulting files will be raw and not prg. + + mem -lnone => raw -d -b a.out,0,-2 + mem -f -lnone => raw -d a.out,2 + mem => raw -d -b a.out,2,-2 + mem -f => raw -d a.out,4 + level => raw -d -b -r a.out,2 + level -f => raw -d a.out,2 + +------------------------------------------------------------------------------- +The command line syntax differs from v1.x. Here's a quick translation guide: +old new +------------------ ----------------------------- +exomizer -ssys exomizer sfx sys +exomizer -s$1234 exomizer sfx $1234 +exomizer -r exomizer level +exomizer exomizer mem -lnone +exomizer -l$1234 exomizer mem -l$1234 +exomizer -lauto exomizer mem +------------------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exo31info.txt b/loader/tools/exomizer-3.1/exo31info.txt new file mode 100644 index 0000000..09d526f --- /dev/null +++ b/loader/tools/exomizer-3.1/exo31info.txt @@ -0,0 +1,210 @@ + +This document only mentions what has changed since exomizer 2.0. For +more general exomizer info please read the exo20info.txt file which also +might contain updated information about previously existing features. + +Exomizer 3.1 +------------ + +New features since 3.0 +---------------------- +o Improved compression on average due to offset reuse. The bit stream format + for crunched files has been extended with a new bit to control this: + + bit 5 Decides if we may reuse the latest sequence offset after a single + literal byte or a single literal sequence: 1=enable, 0=disable + + This featue is enabled by default so the raw, mem and level modes now + default to -P39. + +o The flags -e and -E have been extended to support split encoding. This + means that the encoding used by a crunched file is provided by a file of + its own and no longer included in the crunched file itself. + + This can save space if several crunched files contain similar data and can + share the encoding. To facilitate this The -E flag now enables the mem, + level and raw sub command to generate a header that is optimized for all + the given input files. See the -E documentation in exo20info.txt for more + details. Support for this has been added to exodecr/exodecrunch.s but is + disabled by default. + +o The exodecr/exodecrunch.s 6502 decruncher now supports forward decrunching. + There are also versions for dasm and acme too. They implement the same + feature set as the ca65 version. + +o Changes to contributed decrunchers: + Added ARM 32bit thumb2 source code by ALeX Kazik + Added Intel 8080 source code by Ivan Gorodetsky + Updated Zilog Z80 source code by Antonio Villena + +o The -P flag has been extended to make it possible to only affect certain + bits by prefixing a value with a - or a + character. This will modify the + current value using AND or OR respectively instead of replacing it. E.g. + -P+16 will set bit 4 but leave the other bits as they were. Likewise, -P-16 + clears bit 4 but leave the others. The syntax also allows for chaining + multiple operations. E.g. -P-32+16 is allowed. An initial absolute value is + also accepted like this -P43-32+16 . + +o Miscellaneous bug fixes, See changelog.txt for details. + +New features since 2.0 +--------------------- + +o New bit stream format for crunched files that allow for faster decrunching. + +o The bit stream format is controlled by a new -P flag. The bits + control different aspects of the bit stream format. -P0 disables all new + aspects and will generate exomizer 2.0 compatible files. The raw, mem and + level modes default to -P7. (-P39 and new bit 5 since 3.1) + + bit 0 Controls bit stream bit orientation, 1=big endian, 0=little endian + bit 1 Controls how more than 7 bits are shifted 1=split into a shift of + less than 8 bits + a byte, 0=all bits are shifted + bit 2 Implicit first literal byte: 1=enable, 0=disable + bit 3 Align bit stream towards start without shift flag bit included: + 1=enable, 0=disable + bit 4 Decides if we are to have two lengths (1 and 2) or three lengths + (1, 2 and 3) using offset tables of their own: 0=two, 1=three + + Please note that many decrunchers has not yet been ported to the new bit + stream format and requires a -P0 on the command-line. However, + exodecrs/exodecrunch.s, rawdecrs/exodecr.c and rawdecrs/exodecrunch.c will + work out of the box. Only the built in decruncher src/exodec.c handles all + possible bit combinations. Please check it out for implementation details. + +o There is a new -T flag that controls traits of the bit stream + that don't affect the bit stream format. Typically traits are disallowed to + make the decruncher faster and/or shorter. The raw mode defaults to -T0. + The mem and level modes default to -T4. + + bit 0 Disallow literal sequences, 0 allow, 1 disallow + bit 1 Disallow sequences with length 1, 0 allow, 1 disallow + bit 2 Disallow sequences with lengths (256*m + n) where m > 0 and n is in + the interval [0 - p] and p is 2 or 3 depending on bit 4 of the + -P, 0 allow, 1 disallow + +o The sfx decruncher and exodecrs/exodecrunch.s take advantage of the new bit + stream format for decrunching speed improvements. They also become faster + if the sequence length is limited to 256 by using -M256 when crunching. + (exodecrs/exodecrunch.s has to be told by a define, see the comments in the + file for more info. It also needs the bit 2 trait (-T4) which is used by + default by the level and mem sub commands but not by raw) + +o The sfx decruncher now recognizes a new symbol, i_perf, that controls the + decruncher size/speed performance trade-off. The valid values are -1 to 2 + where -1 is slowest/shortest and 2 is fastest/largest. Default is 0. They + all are faster than exomizer 2.11 by some margin. None of the decrunchers + will destroy zero-page. + +o The sfx decruncher supports the -P+16 flag to enable a dedicated offset + table for sequences of length 3. Using this flag might increase compression + a bit for some files but will also make the decrunch table larger, 204 + bytes instead of the default 156 bytes. + +o Very slightly improved compression. + + +Contributing +------------ + + You are, as before, very welcome to contribute decruncher source code for + unsupported targets and/or improving existing decrunchers. + + However, The unclear licensing of some of the contributions so far has not + been ideal. So please decide on what license to use before contributing. If + you are unsure about the licensing then I recommend using the same license + that I use for my own decruncher sources. It is a plain zlib license with an + additional BSD like no-endorsement clause. See exodecrs/exodecrunch.s for + how it can be included in the source file as a comment block. + +Sources of inspiration +---------------------- + + The new bit stream format features/traits and the 6502 decruncher speed + improvements have been inspired from several sources that I would like to + mention (in no particular order): + + * The exoopt tool developed by Metalbrain and Antonio Villena + (z80 decruncher contributors, the new -P and -T flags should be able to + replicate all bit protocol variants that exoopt generates). + * The Doynamite 6502 decruncher by Doynax and the subsizer cruncher by TLR, + for raising the bar. + * Feedback from users + +Benchmarks for the improved 6502 decrunchers +-------------------------------------------- + +The measured time is the number of cycles the decruncher needs to run. +Interrupt processing and other kinds of overhead that exists in a typical +computer environment is not included. (M=*1000000 and k=*1000) + +* The benchmark for the sfx decruncher use the file zorrounpacked.prg file +(https://web.archive.org/web/20050102175732/http://www.swolff.dk/cruncher/zorrounpacked.prg) also used in the README.txt of subsizer for comparisons: + +Sorted on size: +| File name | Size | Reduced | Cycles | C/B | B/kC | +|--------------------------------|-------|---------|---------|--------|-------| +| zorro_exo31_-1-P+16.prg | 30231 | 44.12% | 6073989 | 112.27 | 8.91 | +| zorro_exo31_0-P+16.prg | 30246 | 44.10% | 5933882 | 109.68 | 9.12 | +| zorro_exo31_1-P+16.prg | 30254 | 44.08% | 5670418 | 104.81 | 9.54 | +| zorro_exo31_0-M256-P+16.prg | 30267 | 44.06% | 5659124 | 104.60 | 9.56 | +| zorro_exo31_1-M256-P+16.prg | 30279 | 44.03% | 5450866 | 100.75 | 9.93 | +| zorro_exo31_2-M256-P+16.prg | 30305 | 43.99% | 5271433 | 97.43 | 10.26 | +| zorro_exo31_2-M256.prg | 30365 | 43.88% | 5268500 | 97.38 | 10.27 | +| zorro_exo31_2-M256-P-32+16.prg | 30697 | 43.26% | 5123329 | 94.70 | 10.56 | +| zorro_exo31_2-M256-P-32.prg | 30758 | 43.15% | 5120580 | 94.65 | 10.57 | + +Sorted on cycles: +| File name | Size | Reduced | Cycles | C/B | B/kC | +|--------------------------------|-------|---------|---------|--------|-------| +| zorro_exo31_2-M256-P-32.prg | 30758 | 43.15% | 5120580 | 94.65 | 10.57 | +| zorro_exo31_2-M256-P-32+16.prg | 30697 | 43.26% | 5123329 | 94.70 | 10.56 | +| zorro_exo31_2-M256.prg | 30365 | 43.88% | 5268500 | 97.38 | 10.27 | +| zorro_exo31_2-M256-P+16.prg | 30305 | 43.99% | 5271433 | 97.43 | 10.26 | +| zorro_exo31_1-M256-P+16.prg | 30279 | 44.03% | 5450866 | 100.75 | 9.93 | +| zorro_exo31_0-M256-P+16.prg | 30267 | 44.06% | 5659124 | 104.60 | 9.56 | +| zorro_exo31_1-P+16.prg | 30254 | 44.08% | 5670418 | 104.81 | 9.54 | +| zorro_exo31_0-P+16.prg | 30246 | 44.10% | 5933882 | 109.68 | 9.12 | +| zorro_exo31_-1-P+16.prg | 30231 | 44.12% | 6073989 | 112.27 | 8.91 | + +The information in the two tables above has been generatedby the exomizer +desfx -S command. + +* The benchmark for the memory decruncher exodecrunch.s uses the "Pearls for +Pigs" files (http://codebase64.org/doku.php?id=base:compression_benchmarks): + +(get_bits not in-lined, unlimited sequence lengths) +| File name | Size | Reduced | Cycles | C/B | B/kC | +|-----------|-------|---------|----------|--------|-------| +| pfp_1.cru | 2934 | 73.35% | 744298 | 67.61 | 14.79 | +| pfp_2.cru | 2157 | 56.63% | 468603 | 94.23 | 10.61 | +| pfp_3.cru | 1727 | 56.27% | 349649 | 88.54 | 11.29 | +| pfp_4.cru | 3427 | 51.15% | 662205 | 94.38 | 10.59 | +| pfp_5.cru | 19186 | 44.80% | 3517541 | 101.20 | 9.88 | +| pfp_6.cru | 8076 | 74.45% | 1974737 | 62.48 | 16.00 | +| pfp_7.cru | 8644 | 57.61% | 1771969 | 86.90 | 11.51 | +| pfp_8.cru | 2926 | 48.78% | 519864 | 91.00 | 10.99 | +| pfp_9.cru | 5166 | 42.34% | 976430 | 108.98 | 9.18 | +| Total | 54243 | 57.75% | 10985296 | 85.57 | 11.69 | + +(get_bits in-lined, sequence lengths limited to 256 or less, no offset reuse) +| File name | Size | Reduced | Cycles | C/B | B/kC | +|--------------------|-------|---------|----------|-------|-------| +| pfp_1.cruiM256P-32 | 2948 | 73.22% | 672500 | 61.09 | 16.37 | +| pfp_2.cruiM256P-32 | 2199 | 55.78% | 419715 | 84.40 | 11.85 | +| pfp_3.cruiM256P-32 | 1784 | 54.82% | 318707 | 80.71 | 12.39 | +| pfp_4.cruiM256P-32 | 3429 | 51.13% | 583501 | 83.17 | 12.02 | +| pfp_5.cruiM256P-32 | 19586 | 43.65% | 3239878 | 93.21 | 10.73 | +| pfp_6.cruiM256P-32 | 8392 | 73.45% | 1838881 | 58.18 | 17.19 | +| pfp_7.cruiM256P-32 | 8763 | 57.03% | 1607365 | 78.82 | 12.69 | +| pfp_8.cruiM256P-32 | 3073 | 46.21% | 489281 | 85.64 | 11.68 | +| pfp_9.cruiM256P-32 | 5314 | 40.69% | 889859 | 99.31 | 10.07 | +| Total | 55488 | 56.78% | 10059687 | 78.36 | 12.76 | + +You can make these tables yourself by naming the pfp files pfp[1-9].raw, +dropping them in the exodecrs/perf folder and in that folder execute the +following command line: $ make pfp.stat pfp.statiM256P-32 + +(You need to build exomizer in the src folder first and you also need to have +cc65, gcc and gnu make on the path) It works in Linux and in a Windows +mingw/msys2 environment in Windows. diff --git a/loader/tools/exomizer-3.1/exobasic10b2.txt b/loader/tools/exomizer-3.1/exobasic10b2.txt new file mode 100644 index 0000000..1e9a17c --- /dev/null +++ b/loader/tools/exomizer-3.1/exobasic10b2.txt @@ -0,0 +1,52 @@ +------------------------------------------------------------------------------- +Features of the Exobasic 1.0b2 tool: +------------------------------------------------------------------------------- + +1) Renumber the basic lines (standard), -n, (Beta quality) + + The lines are renumbered starting with and increasing with for each + line. + +2) Renumber the basic lines (extreme), -N (Beta quality) + + This will increase the file crunchability. This renumbering will renumber + many basic lines to 0. Yes, they will run correctly. :) + +2) Clobber the line links, -p (Release quality) + + Normally a basic program file consists of a linked list of zero terminated + basic line strings. The links between the lines are redundant and can be + recreated. In fact the basic interpreter always recreate the links when + a basic program is loaded from the READY prompt. This is done to be able to + easily move basic programs between compatible platforms where the start + address of basic differs. The links contained in the file are only correct if + the file is loaded to the address it was saved from. + + This means that we can write any values as the links as long as we recreate + them before running the program. The routine that recreates the links is at + $A533 (c64) or $8818 (c16/plus4). + + The values this option actually writes to the links are optimized to increase + the file crunchability. + +3) Add a trampoline -t, (Release quality) + + This feature adds a small machine language routine at the beginning of the + program. This routine does all the startup preparation work necessary and + then starts the program. If the "clobber line links" option is selected, the + added trampoline will recreate them. When a trampoline is added it is very + easy to crunch the file and then just jmp the trampoline to start it. For a + c16/plus4 trampoline add a -4 option to the commandline as well. + +4) Remove rem statements and unnecessary spaces, -r (Release quality) + + This feature removes rem statements unless they are first on a line and the + line is a goto/gosub target. If so then only the rem statement is left. + It also removes unneccessary spaces between basic statements. + +5) Regenerate c16/plus4 stack color table, -c (Release quality) + This feature only works if a c16/plus4 trampoline is created with the -t and + -4 flags. It will regenerate a system color table located at the hardware + stack that the Exomisers built-in decruncher overwrites. + +------------------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/Makefile b/loader/tools/exomizer-3.1/exodecrs/Makefile new file mode 100644 index 0000000..970331e --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/Makefile @@ -0,0 +1,87 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../src/6502emu.o ../src/exo_util.o ../src/log.o ../src/areatrace.o ../src/vec.o ../src/buf_io.o ../src/buf.o ../src/table.o ../src/perf.o + +SOURCES1 = main1.os exostreamdecr1.os +SOURCES2 = main2.os exostreamdecr2.os + +NAME1 = test.1stream.prg +NAME2 = test.2stream.prg + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i e M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ e^-DENABLE_SPLIT_ENCODING=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.exo %stream.exo %.os test%.prg $(TEST_OBJS) +.SECONDEXPANSION: + +build: testrun $$(foreach variant, $$(VARIANTS) 1stream 2stream, test.$$(variant).prg data.$$(variant).exo) + @$(RM) *.out + @./testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).exo) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).exo) + @./testrun $(foreach variant, 1stream 2stream, test.$(variant).prg data.$(variant).exo@0x3000) + @$(MAKE) $(foreach variant, 1stream 2stream, assert.data.$(variant).exo) + +assert.data%: + cmp -i0:2 data$(call variant, $@).exo.out data.bin + @$(RM) data$(call variant, $@).exo.out + +testrun.test%.prg: testrun test%.prg data%.exo + @./testrun test$(call variant, $@).prg data$(call variant, $@).exo + @cmp -i0:2 data$(call variant, $@).exo.out data.bin && $(RM) data$(call variant, $@).exo.out + +testrun.test%stream.prg: testrun test%stream.prg data%stream.exo + @./testrun test$(call variant, $@).prg data$(call variant, $@).exo@0x3000 + @cmp -i0:2 data$(call variant, $@).exo.out data.bin && $(RM) data$(call variant, $@).exo.out + +testrun: testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ testrun.o $(TEST_OBJS) + +test%.prg: main%.os exodecrunch%.os + @echo "building $@" + @ld65 main$(call variant, $@).os exodecrunch$(call variant, $@).os -o $@ -Cc64.cfg + +$(NAME1): $(SOURCES1) + @ld65 $(SOURCES1) -o $@ -Cc64.cfg + +$(NAME2): $(SOURCES2) + @ld65 $(SOURCES2) -o $@ -Cc64.cfg + +%.os: $$(basename $$(basename $$@)).s + @ca65 $(call asmopt, $(call variant, $@)) $< -o $@ + +clean: + @$(RM) testrun.o test*.prg *.os *.exo *.exo.out testrun + +%.exo: $$(basename $$(basename $$@)).bin + @../src/exomizer mem $(call exoopt, $(call variant, $@)) -q $<,0x3000 -o $@ + +%stream.exo: $$(basename $$(basename $$@)).bin + @../src/exomizer raw -P0 -b -c -m 1024 -q -C $<,2 -o $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/README_exo3.txt b/loader/tools/exomizer-3.1/exodecrs/README_exo3.txt new file mode 100644 index 0000000..4621662 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/README_exo3.txt @@ -0,0 +1,7 @@ + + Please note that not all decrunchers in this folder has not yet been updated +to use the new file format for crunched files that exomizer 3 generates by +default. Those decrunchers are exostreamdecr1.s exostreamdecr2.s. + + To generate files that are compatible with exomizer 2 you must now add the +flag -P0 to the exomizer commandline. diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/Makefile b/loader/tools/exomizer-3.1/exodecrs/acme/Makefile new file mode 100644 index 0000000..4fbce2a --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/Makefile @@ -0,0 +1,68 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s +ACME = acme + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../../src/6502emu.o ../../src/exo_util.o ../../src/log.o ../../src/areatrace.o ../../src/vec.o ../../src/buf_io.o ../../src/buf.o ../../src/table.o ../../src/perf.o + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i e M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ e^-DENABLE_SPLIT_ENCODING=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.exo %.os test%.prg $(TEST_OBJS) +.SECONDEXPANSION: + +build: testrun $$(foreach variant, $$(VARIANTS), test.$$(variant).prg data.$$(variant).exo) + @$(RM) *.out + @./testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).exo) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).exo) + +assert.data%: + cmp -i0:2 data$(call variant, $@).exo.out ../data.bin + @$(RM) data$(call variant, $@).exo.out + +testrun.test%.prg: testrun test%.prg data%.exo + @./testrun test$(call variant, $@).prg data$(call variant, $@).exo + @cmp -i0:2 data$(call variant, $@).exo.out ../data.bin && $(RM) data$(call variant, $@).exo.out + +testrun.test%stream.prg: testrun test%stream.prg data%stream.exo + @./testrun test$(call variant, $@).prg data$(call variant, $@).exo@0x3000 + @cmp -i0:2 data$(call variant, $@).exo.out data.bin && $(RM) data$(call variant, $@).exo.out + +testrun: ../testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ ../testrun.o $(TEST_OBJS) + +test%.prg: main.asm exodecrunch.asm + @echo "building $@" + @$(ACME) -o $@ -f cbm $(call asmopt, $(call variant, $@)) main.asm + +clean: + @$(RM) testrun.o test*.prg *.os *.exo *.exo.out testrun + +%.exo: ../$$(basename $$(basename $$@)).bin + @../../src/exomizer mem $(call exoopt, $(call variant, $@)) -q $<,0x3000 -o $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/exodecrunch.asm b/loader/tools/exomizer-3.1/exodecrs/acme/exodecrunch.asm new file mode 100644 index 0000000..e9d1a15 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/exodecrunch.asm @@ -0,0 +1,567 @@ +; +; Copyright (c) 2002 - 2019 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; Known quirks: +; Can't handle a sequence reference that ends at $ffff. It is left in +; since it is a corner case and fixing it impacts negatively on +; performance or backwards compatibility. +; A simple way to work around this is to not decrunch to address $ffff. +; ------------------------------------------------------------------- +; Controls if the shared get_bits routines should be inlined or not. +;INLINE_GET_BITS=1 +!ifndef INLINE_GET_BITS { +INLINE_GET_BITS = 0 +} +; ------------------------------------------------------------------- +; if literal sequences is not used (the data was crunched with the -c +; flag) then the following line can be uncommented for shorter and. +; slightly faster code. +;LITERAL_SEQUENCES_NOT_USED = 1 +!ifndef LITERAL_SEQUENCES_NOT_USED { +LITERAL_SEQUENCES_NOT_USED = 0 +} +; ------------------------------------------------------------------- +; if the sequence length is limited to 256 (the data was crunched with +; the -M256 flag) then the following line can be uncommented for +; shorter and slightly faster code. +;MAX_SEQUENCE_LENGTH_256 = 1 +!ifndef MAX_SEQUENCE_LENGTH_256 { +MAX_SEQUENCE_LENGTH_256 = 0 +} +; ------------------------------------------------------------------- +; if the sequence length 3 has its own offset table (the data was +; crunched with the -P+16 flag) then the following +; line must be uncommented. +;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1 +!ifndef EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE { +EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0 +} +; ------------------------------------------------------------------- +; if sequence offsets are not reused (the data was crunched with the +; -P-32 flag) then the following line must be uncommented. Uncommenting the +; line will also result in shorter and slightly faster code. +;DONT_REUSE_OFFSET = 1 +!ifndef DONT_REUSE_OFFSET { +DONT_REUSE_OFFSET = 0 +} +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +!ifndef DECRUNCH_FORWARDS { +DECRUNCH_FORWARDS = 0 +} +; ------------------------------------------------------------------- +; if split encoding is used (the data is crunched with the -E flag) +; then the following line must be uncommented. +;ENABLE_SPLIT_ENCODING = 1 +!ifndef ENABLE_SPLIT_ENCODING { +ENABLE_SPLIT_ENCODING = 0 +} +; ------------------------------------------------------------------- +; The decruncher jsr:s to the exod_get_crunched_byte address when it wants to +; read a crunched byte into A. This subroutine has to preserve X and Y +; register and must not modify the state of the carry nor the overflow flag. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; The exod_decrunch function is the heart of the decruncher. +; It initializes the decruncher zeropage locations and precalculates the +; decrunch tables and decrunches the data +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- +!if ENABLE_SPLIT_ENCODING != 0 { +; ------------------------------------------------------------------- +; To decrunch files crunched with the split feature (-E) you can't use the +; decrunch function. Instead you call the split_decrunch function. But you +; can only do this if the decrunch table contains the encoding used by the +; file you are decrunching. To generate the correct content for the decrunch +; table call set the get_crunched_byte function to point to the encoding data +; and then call the split_gentable function. +; ------------------------------------------------------------------- +} +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +exod_zp_len_lo = $9e +exod_zp_len_hi = $9f + +exod_zp_src_lo = $ae +exod_zp_src_hi = exod_zp_src_lo + 1 + +exod_zp_bits_hi = $a7 +!if DONT_REUSE_OFFSET = 0 { +exod_zp_ro_state = $a8 +} + +exod_zp_bitbuf = $fd +exod_zp_dest_lo = exod_zp_bitbuf + 1 ; dest addr lo +exod_zp_dest_hi = exod_zp_bitbuf + 2 ; dest addr hi + +!zone exodecrunch +!if EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 { +.encoded_entries = 68 +} else { +.encoded_entries = 52 +} + +.tabl_bi = exod_decrunch_table +.tabl_lo = exod_decrunch_table + .encoded_entries +.tabl_hi = exod_decrunch_table + .encoded_entries * 2 + + ;; refill bits is always inlined +!macro exod_mac_refill_bits { + pha + jsr exod_get_crunched_byte + rol + sta exod_zp_bitbuf + pla +} + +!macro exod_mac_get_bits { +!if INLINE_GET_BITS != 0 { + adc #$80 ; needs c=0, affects v + asl + bpl .gb_skip +.gb_next: + asl exod_zp_bitbuf + bne .gb_ok + +exod_mac_refill_bits +.gb_ok: + rol + bmi .gb_next +.gb_skip: + bvc .skip +.gb_get_hi: + sec + sta exod_zp_bits_hi + jsr exod_get_crunched_byte +.skip: +} else { + jsr exod_get_bits +} +} + +!macro exod_mac_init_zp { +; ------------------------------------------------------------------- +; init zeropage and x reg. (8 bytes) +; +.init_zp: + jsr exod_get_crunched_byte + sta exod_zp_bitbuf - 1,x + dex + bne .init_zp +} + +!if INLINE_GET_BITS = 0 { +exod_get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl .gb_skip +.gb_next: + asl exod_zp_bitbuf + bne .gb_ok + +exod_mac_refill_bits +.gb_ok: + rol + bmi .gb_next +.gb_skip: + bvs .gb_get_hi + rts +.gb_get_hi: + sec + sta exod_zp_bits_hi + jmp exod_get_crunched_byte +} +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to decrunch, it will in turn init the tables and +; call the decruncher +; no constraints on register content, however the +; decimal flag has to be cleared (it almost always is, otherwise do a cld) +exod_decrunch: +!if ENABLE_SPLIT_ENCODING != 0 { + ldx #3 + jsr .internal_gentable + jmp .normal_decrunch +exod_split_gentable: + ldx #1 +.internal_gentable: + jsr .split_init_zp +} else { + ldx #3 + +exod_mac_init_zp +} +; ------------------------------------------------------------------- +; calculate tables (64 bytes) + get_bits macro +; x and y must be #0 when entering +; + ldy #0 + clc +.table_gen: + tax + tya + and #$0f + sta .tabl_lo,y + beq .shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc .tabl_lo - 1,y + sta .tabl_lo,y + lda exod_zp_len_hi + adc .tabl_hi - 1,y +.shortcut: + sta .tabl_hi,y +; ------------------------------------------------------------------- + lda #$01 + sta C=0, no reuse + bne .copy_next ; bit != 0 => C=0, reuse previous offset +} +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +.exit_or_lit_seq: +!if LITERAL_SEQUENCES_NOT_USED = 0 { + beq .decr_exit + jsr exod_get_crunched_byte +!if MAX_SEQUENCE_LENGTH_256 = 0 { + sta exod_zp_len_hi +} + jsr exod_get_crunched_byte + tax + bcs .copy_next +.decr_exit: +} + rts +!if LITERAL_SEQUENCES_NOT_USED = 0 { +.get_literal_byte: + jsr exod_get_crunched_byte + bcs .literal_byte_gotten +} +!if EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 { +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes) +; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to +; %10010000, %11100011, %11100010 +.tabl_bit: + !byte $90, $e3, $e2 +} else { +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1 and 2 (2 bytes) +; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010 +.tabl_bit: + !byte $8c, $e2 +} + +!if ENABLE_SPLIT_ENCODING != 0 { +.split_init_zp: + +exod_mac_init_zp + rts +} +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; this 156 (204) byte table area may be relocated. It may also be +; clobbered by other data between decrunches. +; ------------------------------------------------------------------- +exod_decrunch_table: + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +!if EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 { + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +} + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + !byte 0,0,0,0,0,0,0,0,0,0,0,0 +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/main.asm b/loader/tools/exomizer-3.1/exodecrs/acme/main.asm new file mode 100644 index 0000000..ee2285e --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/main.asm @@ -0,0 +1,54 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled with ACME. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +!ifndef DECRUNCH_FORWARDS { +DECRUNCH_FORWARDS = 0 +} +; ------------------------------------------------------------------- +* = $0801 + !byte $0b,$08,<2019,>2019,$9e,$32,$30,$36,$31,0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- +!if DECRUNCH_FORWARDS = 0 { + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi +} else { + lda $02 + sta _byte_lo + lda $03 + sta _byte_hi +} + jmp exod_decrunch +; ------------------------------------------------------------------- +exod_get_crunched_byte: +!if DECRUNCH_FORWARDS = 0 { + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +} +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before +!if DECRUNCH_FORWARDS != 0 { + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: +} + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- + !source "exodecrunch.asm" diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/split/Makefile b/loader/tools/exomizer-3.1/exodecrs/acme/split/Makefile new file mode 100644 index 0000000..3de1e95 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/split/Makefile @@ -0,0 +1,70 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s +ACME = acme + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../../../src/6502emu.o ../../../src/exo_util.o ../../../src/log.o ../../../src/areatrace.o ../../../src/vec.o ../../../src/buf_io.o ../../../src/buf.o ../../../src/table.o ../../../src/perf.o + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = -DENABLE_SPLIT_ENCODING=1 $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.os %.exo %.prg %.out $(TEST_OBJS) +.SECONDEXPANSION: + +build: ../../testrun $$(foreach variant, $$(VARIANTS), test.$$(variant).prg data.$$(variant).prg) + @$(RM) *.out + @../../testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).prg) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).prg) + +assert.data%: + cmp -i0:2 data$(call variant, $@).prg.out ../../data.bin + @$(RM) data$(call variant, $@).prg.out + +testrun.test%.prg: ../../testrun test%.prg data%.prg + @../../testrun test$(call variant, $@).prg data$(call variant, $@).prg + @cmp -i0:2 data$(call variant, $@).prg.out ../../data.bin && $(RM) data$(call variant, $@).prg.out + +../../testrun: ../../testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ ../testrun.o $(TEST_OBJS) + +test%.prg: main.asm ../exodecrunch.asm + @echo "building $@" + @$(ACME) -o $@ -f cbm $(call asmopt, $(call variant, $@)) main.asm + +clean: + @$(RM) testrun.o *.prg *.out *.os *.exo* testrun + +data%.prg: data%.asm + @$(ACME) -o $@ -f cbm $(call asmopt, $(call variant, $@)) $< + +%.exo %.exo.00 %.exo.01: ../../$$(basename $$(basename $$@)).bin + @../../../src/exomizer mem $(call exoopt, $(call variant, $@)) -q -E $<,0x3000,0,0xb00 $<,0x3b00,0xb00 -o $*.exo + +data%.asm: data.asm.template data%.exo data%.exo.00 data%.exo.01 + @sed 's/PLACEHOLDER/data$*.exo/' $< > $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/split/data.asm.template b/loader/tools/exomizer-3.1/exodecrs/acme/split/data.asm.template new file mode 100644 index 0000000..f65ded5 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/split/data.asm.template @@ -0,0 +1,27 @@ +!ifndef DECRUNCH_FORWARDS { +DECRUNCH_FORWARDS = 0 +} +; ------------------------------------------------------------------- +* = $8000 +LOAD_ADDR = $8000 +baseLoad: +!if DECRUNCH_FORWARDS != 0 { + !word tablestart + LOAD_ADDR - baseLoad + !word part0start + LOAD_ADDR - baseLoad + !word part1start + LOAD_ADDR - baseLoad + !word 0 +} else { + !word tableend + LOAD_ADDR - baseLoad + !word part0end + LOAD_ADDR - baseLoad + !word part1end + LOAD_ADDR - baseLoad + !word 0 +} +tablestart: +!binary "PLACEHOLDER" +tableend: +part0start: +!binary "PLACEHOLDER.00" +part0end: +part1start: +!binary "PLACEHOLDER.01" +part1end: diff --git a/loader/tools/exomizer-3.1/exodecrs/acme/split/main.asm b/loader/tools/exomizer-3.1/exodecrs/acme/split/main.asm new file mode 100644 index 0000000..a8eb14a --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/acme/split/main.asm @@ -0,0 +1,77 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +!ifndef DECRUNCH_FORWARDS { +DECRUNCH_FORWARDS = 0 +} +; ------------------------------------------------------------------- +* = $0801 + !byte $0b,$08,<2019,>2019,$9e,$32,$30,$36,$31,0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + lda $02 + sta _ebyte_lo + lda $03 + sta _ebyte_hi + + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr exod_split_gentable + +next_part: + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr exod_split_decrunch + jmp next_part + +done: + rts +; ------------------------------------------------------------------- +get_Emerge_byte: +_ebyte_lo = * + 1 +_ebyte_hi = * + 2 + lda $ffff + inc _ebyte_lo + bne _ebyte_skip_hi + inc _ebyte_hi +_ebyte_skip_hi: + rts +; ------------------------------------------------------------------- +exod_get_crunched_byte: +!if DECRUNCH_FORWARDS = 0 { + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +} +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before +!if DECRUNCH_FORWARDS != 0 { + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: +} + rts ; decrunch_file is called. +; ------------------------------------------------------------------- +!source "../exodecrunch.asm" diff --git a/loader/tools/exomizer-3.1/exodecrs/c64.cfg b/loader/tools/exomizer-3.1/exodecrs/c64.cfg new file mode 100644 index 0000000..b50d308 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/c64.cfg @@ -0,0 +1,10 @@ +MEMORY { +ZP: start = $00, size = $100, type = rw, define = yes; +RAM: start = $07ff, size = $9801, define = yes, file = %O; +} +SEGMENTS { +CODE: load = RAM, type = ro; +DATA: load = RAM, type = rw; +BSS: load = RAM, type = bss, define = yes; +ZEROPAGE: load = ZP, type = zp; +} diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/Makefile b/loader/tools/exomizer-3.1/exodecrs/dasm/Makefile new file mode 100644 index 0000000..f0a8da1 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/Makefile @@ -0,0 +1,68 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s +DASM = dasm + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../../src/6502emu.o ../../src/exo_util.o ../../src/log.o ../../src/areatrace.o ../../src/vec.o ../../src/buf_io.o ../../src/buf.o ../../src/table.o ../../src/perf.o + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i e M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ e^-DENABLE_SPLIT_ENCODING=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.exo %.os test%.prg $(TEST_OBJS) +.SECONDEXPANSION: + +build: ../testrun $$(foreach variant, $$(VARIANTS), test.$$(variant).prg data.$$(variant).exo) + @$(RM) *.out + @../testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).exo) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).exo) + +assert.data%: + cmp -i0:2 data$(call variant, $@).exo.out ../data.bin + @$(RM) data$(call variant, $@).exo.out + +testrun.test%.prg: testrun test%.prg data%.exo + @../testrun test$(call variant, $@).prg data$(call variant, $@).exo + @cmp -i0:2 data$(call variant, $@).exo.out ../data.bin && $(RM) data$(call variant, $@).exo.out + +testrun.test%stream.prg: testrun test%stream.prg data%stream.exo + @../testrun test$(call variant, $@).prg data$(call variant, $@).exo@0x3000 + @cmp -i0:2 data$(call variant, $@).exo.out data.bin && $(RM) data$(call variant, $@).exo.out + +../testrun: ../testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ ../testrun.o $(TEST_OBJS) + +test%.prg: main.s exodecrunch.s + @echo "building $@" + @$(DASM) main.s $(call asmopt, $(call variant, $@)) -o$@ + +clean: + @$(RM) testrun.o test*.prg *.os *.exo *.exo.out testrun + +%.exo: ../$$(basename $$(basename $$@)).bin + @../../src/exomizer mem $(call exoopt, $(call variant, $@)) -q $<,0x3000 -o $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/exodecrunch.s b/loader/tools/exomizer-3.1/exodecrs/dasm/exodecrunch.s new file mode 100644 index 0000000..ca697d8 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/exodecrunch.s @@ -0,0 +1,569 @@ +; +; Copyright (c) 2002 - 2020 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; Known quirks: +; Can't handle a sequence reference that ends at $ffff. It is left in +; since it is a corner case and fixing it impacts negatively on +; performance or backwards compatibility. +; A simple way to work around this is to not decrunch to address $ffff. +; ------------------------------------------------------------------- +; Controls if the shared get_bits routines should be inlined or not. +;INLINE_GET_BITS=1 + IFNCONST INLINE_GET_BITS +INLINE_GET_BITS = 0 + ENDIF +; ------------------------------------------------------------------- +; if literal sequences is not used (the data was crunched with the -c +; flag) then the following line can be uncommented for shorter and. +; slightly faster code. +;LITERAL_SEQUENCES_NOT_USED = 1 + IFNCONST LITERAL_SEQUENCES_NOT_USED +LITERAL_SEQUENCES_NOT_USED = 0 + ENDIF +; ------------------------------------------------------------------- +; if the sequence length is limited to 256 (the data was crunched with +; the -M256 flag) then the following line can be uncommented for +; shorter and slightly faster code. +;MAX_SEQUENCE_LENGTH_256 = 1 + IFNCONST MAX_SEQUENCE_LENGTH_256 +MAX_SEQUENCE_LENGTH_256 = 0 + ENDIF +; ------------------------------------------------------------------- +; if the sequence length 3 has its own offset table (the data was +; crunched with the -P+16 flag) then the following +; line must be uncommented. +;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1 + IFNCONST EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE +EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0 + ENDIF +; ------------------------------------------------------------------- +; if sequence offsets are not reused (the data was crunched with the +; -P-32 flag) then the following line must be uncommented. Uncommenting the +; line will also result in shorter and slightly faster code. +;DONT_REUSE_OFFSET = 1 + IFNCONST DONT_REUSE_OFFSET +DONT_REUSE_OFFSET = 0 + ENDIF +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 + IFNCONST DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 + ENDIF +; ------------------------------------------------------------------- +; if split encoding is used (the data is crunched with the -E flag) +; then the following line must be uncommented. +;ENABLE_SPLIT_ENCODING = 1 + IFNCONST ENABLE_SPLIT_ENCODING +ENABLE_SPLIT_ENCODING = 0 + ENDIF + +; ------------------------------------------------------------------- +; The decruncher jsr:s to the exod_get_crunched_byte address when it wants to +; read a crunched byte into A. This subroutine has to preserve X and Y +; register and must not modify the state of the carry nor the overflow flag. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; The exod_decrunch function is the heart of the decruncher. (for non split +; crunched files) +; It initializes the decruncher zeropage locations and precalculates the +; decrunch tables and decrunches the data +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- + IF ENABLE_SPLIT_ENCODING != 0 +; ------------------------------------------------------------------- +; To decrunch files crunched with the split feature (-E) you can't use the +; decrunch function. Instead you call the split_decrunch function. But you +; can only do this if the decrunch table contains the encoding used by the +; file you are decrunching. To generate the correct content for the decrunch +; table call set the get_crunched_byte function to point to the encoding data +; and then call the split_gentable function. +; ------------------------------------------------------------------- + ENDIF +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +exod_zp_len_lo = $9e +exod_zp_len_hi = $9f + +exod_zp_src_lo = $ae +exod_zp_src_hi = exod_zp_src_lo + 1 + +exod_zp_bits_hi = $a7 + IF DONT_REUSE_OFFSET == 0 +exod_zp_ro_state = $a8 + ENDIF + +exod_zp_bitbuf = $fd +exod_zp_dest_lo = exod_zp_bitbuf + 1 ; dest addr lo +exod_zp_dest_hi = exod_zp_bitbuf + 2 ; dest addr hi + + SUBROUTINE exodecrunch + IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 +.encoded_entries = 68 + ELSE +.encoded_entries = 52 + ENDIF + +.tabl_bi = exod_decrunch_table +.tabl_lo = exod_decrunch_table + .encoded_entries +.tabl_hi = exod_decrunch_table + .encoded_entries * 2 + + ;; refill bits is always inlined + MAC exod_mac_refill_bits + pha + jsr exod_get_crunched_byte + rol + sta exod_zp_bitbuf + pla + ENDM + + MAC exod_mac_get_bits + IF INLINE_GET_BITS != 0 + adc #$80 ; needs c=0, affects v + asl + bpl .gb_skip +.gb_next: + asl exod_zp_bitbuf + bne .gb_ok + exod_mac_refill_bits +.gb_ok: + rol + bmi .gb_next +.gb_skip: + bvc .skip +.gb_get_hi: + sec + sta exod_zp_bits_hi + jsr exod_get_crunched_byte +.skip: + ELSE + jsr exod_get_bits + ENDIF + ENDM + + MAC exod_mac_init_zp +; ------------------------------------------------------------------- +; init zeropage and x reg. (8 bytes) +; +.init_zp: + jsr exod_get_crunched_byte + sta exod_zp_bitbuf - 1,x + dex + bne .init_zp + ENDM + + IF INLINE_GET_BITS == 0 +exod_get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl .gb_skip +.gb_next: + asl exod_zp_bitbuf + bne .gb_ok + exod_mac_refill_bits +.gb_ok: + rol + bmi .gb_next +.gb_skip: + bvs .gb_get_hi + rts +.gb_get_hi: + sec + sta exod_zp_bits_hi + jmp exod_get_crunched_byte + ENDIF +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to decrunch, it will in turn init the tables and +; call the decruncher +; no constraints on register content, however the +; decimal flag has to be cleared (it almost always is, otherwise do a cld) +exod_decrunch: + IF ENABLE_SPLIT_ENCODING != 0 + ldx #3 + jsr .internal_gentable + jmp .normal_decrunch +exod_split_gentable: + ldx #1 +.internal_gentable: + jsr .split_init_zp + ELSE + ldx #3 + exod_mac_init_zp + ENDIF +; ------------------------------------------------------------------- +; calculate tables (64 bytes) + get_bits macro +; x and y must be #0 when entering +; + ldy #0 + clc +.table_gen: + tax + tya + and #$0f + sta .tabl_lo,y + beq .shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc .tabl_lo - 1,y + sta .tabl_lo,y + lda exod_zp_len_hi + adc .tabl_hi - 1,y +.shortcut: + sta .tabl_hi,y +; ------------------------------------------------------------------- + lda #$01 + sta C=0, no reuse + bne .copy_next ; bit != 0 => C=0, reuse previous offset + ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +.exit_or_lit_seq: + IF LITERAL_SEQUENCES_NOT_USED == 0 + beq .decr_exit + jsr exod_get_crunched_byte + IF MAX_SEQUENCE_LENGTH_256 == 0 + sta exod_zp_len_hi + ENDIF + jsr exod_get_crunched_byte + tax + bcs .copy_next +.decr_exit: + ENDIF + rts + IF LITERAL_SEQUENCES_NOT_USED == 0 +.get_literal_byte: + jsr exod_get_crunched_byte + bcs .literal_byte_gotten + ENDIF + IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes) +; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to +; %10010000, %11100011, %11100010 +.tabl_bit: + .BYTE $90, $e3, $e2 + ELSE +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1 and 2 (2 bytes) +; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010 +.tabl_bit: + .BYTE $8c, $e2 + ENDIF + + IF ENABLE_SPLIT_ENCODING != 0 +.split_init_zp: + exod_mac_init_zp + rts + ENDIF +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; this 156 (204) byte table area may be relocated. It may also be +; clobbered by other data between decrunches. +; ------------------------------------------------------------------- +exod_decrunch_table: + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE != 0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + ENDIF + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0 +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/main.s b/loader/tools/exomizer-3.1/exodecrs/dasm/main.s new file mode 100644 index 0000000..2fb3e06 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/main.s @@ -0,0 +1,55 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled with dasm. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 + IFNCONST DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 + ENDIF +; ------------------------------------------------------------------- + PROCESSOR 6502 + ORG $0801 + .byte $0b,$08,<2019,>2019,$9e,$32,$30,$36,$31,0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + IF DECRUNCH_FORWARDS == 0 + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi + ELSE + lda $02 + sta _byte_lo + lda $03 + sta _byte_hi + ENDIF + jmp exod_decrunch +; ------------------------------------------------------------------- +exod_get_crunched_byte: + IF DECRUNCH_FORWARDS == 0 + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo + ENDIF +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before + IF DECRUNCH_FORWARDS != 0 + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: + ENDIF + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- + INCLUDE exodecrunch.s diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/split/Makefile b/loader/tools/exomizer-3.1/exodecrs/dasm/split/Makefile new file mode 100644 index 0000000..12af84a --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/split/Makefile @@ -0,0 +1,70 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s +DASM = dasm + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../../../src/6502emu.o ../../../src/exo_util.o ../../../src/log.o ../../../src/areatrace.o ../../../src/vec.o ../../../src/buf_io.o ../../../src/buf.o ../../../src/table.o ../../../src/perf.o + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = -DENABLE_SPLIT_ENCODING=1 $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.os %.exo %.prg %.out $(TEST_OBJS) +.SECONDEXPANSION: + +build: ../../testrun $$(foreach variant, $$(VARIANTS), test.$$(variant).prg data.$$(variant).prg) + @$(RM) *.out + @../../testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).prg) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).prg) + +assert.data%: + cmp -i0:2 data$(call variant, $@).prg.out ../../data.bin + @$(RM) data$(call variant, $@).prg.out + +testrun.test%.prg: ../../testrun test%.prg data%.prg + @../../testrun test$(call variant, $@).prg data$(call variant, $@).prg + @cmp -i0:2 data$(call variant, $@).prg.out ../../data.bin && $(RM) data$(call variant, $@).prg.out + +../../testrun: ../../testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ ../testrun.o $(TEST_OBJS) + +test%.prg: main.s ../exodecrunch.s + @echo "building $@" + @$(DASM) $< $(call asmopt, $(call variant, $@)) -o$@ + +clean: + @$(RM) testrun.o *.prg *.out *.os *.exo* testrun + +data%.prg: data%.s + @$(DASM) $< $(call asmopt, $(call variant, $@)) -o$@ + +%.exo %.exo.00 %.exo.01: ../../$$(basename $$(basename $$@)).bin + @../../../src/exomizer mem $(call exoopt, $(call variant, $@)) -q -E $<,0x3000,0,0xb00 $<,0x3b00,0xb00 -o $*.exo + +data%.s: data.s.template data%.exo data%.exo.00 data%.exo.01 + @sed 's/PLACEHOLDER/data$*.exo/' $< > $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/split/data.s.template b/loader/tools/exomizer-3.1/exodecrs/dasm/split/data.s.template new file mode 100644 index 0000000..458e106 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/split/data.s.template @@ -0,0 +1,28 @@ + IFNCONST DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 + ENDIF +; ------------------------------------------------------------------- + PROCESSOR 6502 + ORG $8000 +LOAD_ADDR = $8000 +baseLoad: + IF DECRUNCH_FORWARDS != 0 + .word tablestart + LOAD_ADDR - baseLoad + .word part0start + LOAD_ADDR - baseLoad + .word part1start + LOAD_ADDR - baseLoad + .word 0 + ELSE + .word tableend + LOAD_ADDR - baseLoad + .word part0end + LOAD_ADDR - baseLoad + .word part1end + LOAD_ADDR - baseLoad + .word 0 + ENDIF +tablestart: + INCBIN "PLACEHOLDER" +tableend: +part0start: + INCBIN "PLACEHOLDER.00" +part0end: +part1start: + INCBIN "PLACEHOLDER.01" +part1end: diff --git a/loader/tools/exomizer-3.1/exodecrs/dasm/split/main.s b/loader/tools/exomizer-3.1/exodecrs/dasm/split/main.s new file mode 100644 index 0000000..aa67734 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/dasm/split/main.s @@ -0,0 +1,78 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 + IFNCONST DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 + ENDIF +; ------------------------------------------------------------------- + PROCESSOR 6502 + ORG $0801 + .byte $0b,$08,<2019,>2019,$9e,$32,$30,$36,$31,0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + lda $02 + sta _ebyte_lo + lda $03 + sta _ebyte_hi + + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr exod_split_gentable + +next_part: + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr exod_split_decrunch + jmp next_part + +done: + rts +; ------------------------------------------------------------------- +get_Emerge_byte: +_ebyte_lo = * + 1 +_ebyte_hi = * + 2 + lda $ffff + inc _ebyte_lo + bne _ebyte_skip_hi + inc _ebyte_hi +_ebyte_skip_hi: + rts +; ------------------------------------------------------------------- +exod_get_crunched_byte: + IF DECRUNCH_FORWARDS == 0 + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo + ENDIF +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before + IF DECRUNCH_FORWARDS != 0 + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: + ENDIF + rts ; decrunch_file is called. +; ------------------------------------------------------------------- + INCLUDE ../exodecrunch.s diff --git a/loader/tools/exomizer-3.1/exodecrs/data.bin b/loader/tools/exomizer-3.1/exodecrs/data.bin new file mode 100644 index 0000000..71ccfe8 Binary files /dev/null and b/loader/tools/exomizer-3.1/exodecrs/data.bin differ diff --git a/loader/tools/exomizer-3.1/exodecrs/exodecrunch.s b/loader/tools/exomizer-3.1/exodecrs/exodecrunch.s new file mode 100644 index 0000000..3dff8be --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/exodecrunch.s @@ -0,0 +1,575 @@ +; +; Copyright (c) 2002 - 2020 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; Known quirks: +; Can't handle a sequence reference that ends at $ffff. It is left in +; since it is a corner case and fixing it impacts negatively on +; performance or backwards compatibility. +; A simple way to work around this is to not decrunch to address $ffff. +; ------------------------------------------------------------------- +; Controls if the shared get_bits routines should be inlined or not. +;INLINE_GET_BITS=1 +.IFNDEF INLINE_GET_BITS +INLINE_GET_BITS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if literal sequences is not used (the data was crunched with the -c +; flag) then the following line can be uncommented for shorter and. +; slightly faster code. +;LITERAL_SEQUENCES_NOT_USED = 1 +.IFNDEF LITERAL_SEQUENCES_NOT_USED +LITERAL_SEQUENCES_NOT_USED = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length is limited to 256 (the data was crunched with +; the -M256 flag) then the following line can be uncommented for +; shorter and slightly faster code. +;MAX_SEQUENCE_LENGTH_256 = 1 +.IFNDEF MAX_SEQUENCE_LENGTH_256 +MAX_SEQUENCE_LENGTH_256 = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length 3 has its own offset table (the data was +; crunched with the -P+16 flag) then the following +; line must be uncommented. +;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1 +.IFNDEF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE +EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0 +.ENDIF +; ------------------------------------------------------------------- +; if sequence offsets are not reused (the data was crunched with the +; -P-32 flag) then the following line must be uncommented. Uncommenting the +; line will also result in shorter and slightly faster code. +;DONT_REUSE_OFFSET = 1 +.IFNDEF DONT_REUSE_OFFSET +DONT_REUSE_OFFSET = 0 +.ENDIF +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if split encoding is used (the data is crunched with the -E flag) +; then the following line must be uncommented. +;ENABLE_SPLIT_ENCODING = 1 +.IFNDEF ENABLE_SPLIT_ENCODING +ENABLE_SPLIT_ENCODING = 0 +.ENDIF + +; ------------------------------------------------------------------- +; The decruncher jsr:s to the get_crunched_byte address when it wants to +; read a crunched byte into A. This subroutine has to preserve X and Y +; register and must not modify the state of the carry nor the overflow flag. +; ------------------------------------------------------------------- +.import get_crunched_byte +; ------------------------------------------------------------------- +; This function is the heart of the decruncher. (for non split crunched files) +; It initializes the decruncher zeropage locations and precalculates the +; decrunch tables and decrunches the data +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- +.export decrunch +.IF ENABLE_SPLIT_ENCODING <> 0 +; ------------------------------------------------------------------- +; To decrunch files crunched with the split feature (-E) you can't use the +; decrunch function. Instead you call the split_decrunch function. But you +; can only do this if the decrunch table contains the encoding used by the +; file you are decrunching. To generate the correct content for the decrunch +; table call set the get_crunched_byte function to point to the encoding data +; and then call the split_gentable function. +; ------------------------------------------------------------------- +.export split_gentable +.export split_decrunch +.ENDIF +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +zp_len_lo = $9e +zp_len_hi = $9f + +zp_src_lo = $ae +zp_src_hi = zp_src_lo + 1 + +zp_bits_hi = $a7 +.IF DONT_REUSE_OFFSET = 0 +zp_ro_state = $a8 +.ENDIF + +zp_bitbuf = $fd +zp_dest_lo = zp_bitbuf + 1 ; dest addr lo +zp_dest_hi = zp_bitbuf + 2 ; dest addr hi + +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +encoded_entries = 68 +.ELSE +encoded_entries = 52 +.ENDIF + +tabl_bi = decrunch_table +tabl_lo = decrunch_table + encoded_entries +tabl_hi = decrunch_table + encoded_entries * 2 + + ;; refill bits is always inlined +.MACRO mac_refill_bits + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +.ENDMACRO + +.MACRO mac_get_bits +.IF INLINE_GET_BITS <> 0 +.SCOPE + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvc skip +gb_get_hi: + sec + sta zp_bits_hi + jsr get_crunched_byte +skip: +.ENDSCOPE +.ELSE + jsr get_bits +.ENDIF +.ENDMACRO + +.MACRO mac_init_zp +.SCOPE +; ------------------------------------------------------------------- +; init zeropage and x reg. (8 bytes) +; +init_zp: + jsr get_crunched_byte + sta zp_bitbuf - 1,x + dex + bne init_zp +.ENDSCOPE +.ENDMACRO + +.IF INLINE_GET_BITS = 0 +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvs gb_get_hi + rts +gb_get_hi: + sec + sta zp_bits_hi + jmp get_crunched_byte +.ENDIF +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to decrunch, it will in turn init the tables and +; call the decruncher +; no constraints on register content, however the +; decimal flag has to be cleared (it almost always is, otherwise do a cld) +decrunch: +.IF ENABLE_SPLIT_ENCODING <> 0 + ldx #3 + jsr internal_gentable + jmp normal_decrunch +split_gentable: + ldx #1 +internal_gentable: + jsr split_init_zp +.ELSE + ldx #3 + mac_init_zp +.ENDIF +; ------------------------------------------------------------------- +; calculate tables (64 bytes) + get_bits macro +; x must be #0 when entering +; + ldy #0 + clc +table_gen: + tax + tya + and #$0f + sta tabl_lo,y + beq shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc tabl_lo - 1,y + sta tabl_lo,y + lda zp_len_hi + adc tabl_hi - 1,y +shortcut: + sta tabl_hi,y +; ------------------------------------------------------------------- + lda #$01 + sta 0 + rts +split_decrunch: + ldx #3 + jsr split_init_zp +; X reg must be 0 here + sec +normal_decrunch: +.ENDIF +; ------------------------------------------------------------------- +; prepare for main decruncher +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state + sec +.ENDIF + ldy zp_dest_lo + stx zp_dest_lo + stx zp_bits_hi +; ------------------------------------------------------------------- +; copy one literal byte to destination (11 bytes) +; +literal_start1: +.IF DECRUNCH_FORWARDS = 0 + tya + bne no_hi_decr + dec zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + dec zp_src_hi +.ENDIF +no_hi_decr: + dey +.ENDIF + jsr get_crunched_byte + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne skip_hi_incr + inc zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + inc zp_src_hi +.ENDIF +skip_hi_incr: +.ENDIF +; ------------------------------------------------------------------- +; fetch sequence length index (15 bytes) +; x must be #0 when entering and contains the length index + 1 +; when exiting or 0 for literal byte +next_round: +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state +.ENDIF + dex + lda zp_bitbuf +no_literal1: + asl + bne nofetch8 + jsr get_crunched_byte + rol +nofetch8: + inx + bcc no_literal1 + sta zp_bitbuf +; ------------------------------------------------------------------- +; check for literal byte (2 bytes) +; + beq literal_start1 +; ------------------------------------------------------------------- +; check for decrunch done and literal sequences (4 bytes) +; + cpx #$11 +.IF INLINE_GET_BITS <> 0 + bcc skip_jmp + jmp exit_or_lit_seq +skip_jmp: +.ELSE + bcs exit_or_lit_seq +.ENDIF +; ------------------------------------------------------------------- +; calulate length of sequence (zp_len) (18(11) bytes) + get_bits macro +; + lda tabl_bi - 1,x + mac_get_bits + adc tabl_lo - 1,x ; we have now calculated zp_len_lo + sta zp_len_lo +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_bits_hi + adc tabl_hi - 1,x ; c = 0 after this. + sta zp_len_hi +; ------------------------------------------------------------------- +; here we decide what offset table to use (27(26) bytes) + get_bits_nc macro +; z-flag reflects zp_len_hi here +; + ldx zp_len_lo +.ELSE + tax +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda #0 +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; here we decide to reuse latest offset or not (13(15) bytes) +; + bit 0 + cpx #$04 +.ELSE + cpx #$03 +.ENDIF + bcs gbnc2_next + lda tabl_bit - 1,x +gbnc2_next: + asl zp_bitbuf + bne gbnc2_ok + tax + jsr get_crunched_byte + rol + sta zp_bitbuf + txa +gbnc2_ok: + rol + bcs gbnc2_next + tax +; ------------------------------------------------------------------- +; calulate absolute offset (zp_src) (17 bytes) + get_bits macro +; + lda tabl_bi,x + mac_get_bits +.IF DECRUNCH_FORWARDS = 0 + adc tabl_lo,x + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + adc zp_dest_hi + sta zp_src_hi +.ELSE + clc + adc tabl_lo,x + eor #$ff + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + eor #$ff + adc zp_dest_hi + sta zp_src_hi + clc +.ENDIF +; ------------------------------------------------------------------- +; prepare for copy loop (2 bytes) +; + ldx zp_len_lo +; ------------------------------------------------------------------- +; main copy loop (30 bytes) +; +copy_next: +.IF DECRUNCH_FORWARDS = 0 + tya + bne copy_skip_hi + dec zp_dest_hi + dec zp_src_hi +copy_skip_hi: + dey +.ENDIF +.IF LITERAL_SEQUENCES_NOT_USED = 0 + bcs get_literal_byte +.ENDIF + lda (zp_src_lo),y +literal_byte_gotten: + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne copy_skip_hi + inc zp_dest_hi + inc zp_src_hi +copy_skip_hi: +.ENDIF + dex + bne copy_next +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_len_hi +.IF INLINE_GET_BITS <> 0 + bne copy_next_hi +.ENDIF +.ENDIF + stx zp_bits_hi +.IF INLINE_GET_BITS = 0 + beq next_round +.ELSE + jmp next_round +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 +copy_next_hi: + dec zp_len_hi + jmp copy_next +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; test for offset reuse (11 bytes) +; +test_reuse: + bvs no_reuse +.IF MAX_SEQUENCE_LENGTH_256 <> 0 + lda #$00 ; fetch one bit +.ENDIF + asl zp_bitbuf + bne gbnc1_ok + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +gbnc1_ok: + rol + beq no_reuse ; bit == 0 => C=0, no reuse + bne copy_next ; bit != 0 => C=0, reuse previous offset +.ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +exit_or_lit_seq: +.IF LITERAL_SEQUENCES_NOT_USED = 0 + beq decr_exit + jsr get_crunched_byte +.IF MAX_SEQUENCE_LENGTH_256 = 0 + sta zp_len_hi +.ENDIF + jsr get_crunched_byte + tax + bcs copy_next +decr_exit: +.ENDIF + rts +.IF LITERAL_SEQUENCES_NOT_USED = 0 +get_literal_byte: + jsr get_crunched_byte + bcs literal_byte_gotten +.ENDIF +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes) +; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to +; %10010000, %11100011, %11100010 +tabl_bit: + .BYTE $90, $e3, $e2 +.ELSE +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1 and 2 (2 bytes) +; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010 +tabl_bit: + .BYTE $8c, $e2 +.ENDIF + +.IF ENABLE_SPLIT_ENCODING <> 0 +split_init_zp: + mac_init_zp + rts +.ENDIF +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; this 156 (204) byte table area may be relocated. It may also be +; clobbered by other data between decrunches. +; ------------------------------------------------------------------- +decrunch_table: + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.ENDIF + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0 +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/exostreamdecr1.s b/loader/tools/exomizer-3.1/exodecrs/exostreamdecr1.s new file mode 100644 index 0000000..90abe54 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/exostreamdecr1.s @@ -0,0 +1,338 @@ +; +; Copyright (c) 2002, 2003 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; + +; ------------------------------------------------------------------- +; the circular buffer needs to be page aligned, so only the high-bytes +; of the buffer start and len is imported to enforce this. +; the data that are to be decrunched must not have been crunched with +; a maximum offset size greater than the buffer size. use the -m option +; with a value of (buffer_len_hi * 256) or less. +; ------------------------------------------------------------------- +.import buffer_start_hi: absolute +.import buffer_len_hi: absolute + +; ------------------------------------------------------------------- +; The decruncher jsr:s to the get_crunched_byte address when it wants to +; read a crunched byte. This subroutine has to preserve x and y register +; and must not modify the state of the carry flag. +; ------------------------------------------------------------------- +.import get_crunched_byte + +; ------------------------------------------------------------------- +; this subsoutine is called before decrunching. It initializes the +; decruncher zeropage locations and precalculates the decrunch tables. +; ------------------------------------------------------------------- +.export init_decruncher + +; ------------------------------------------------------------------- +; this function is the heart of the decruncher. Call this whenever you +; want a decrunched byte. It will return it in the accumulator. It will +; also destroy the values in y and x reg and mess up the status flags. +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- +.export get_decrunched_byte + +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +zp_src_hi = $a7 +zp_src_lo = zp_src_hi + 1 +zp_src_bi = zp_src_hi + 2 +zp_bitbuf = zp_src_hi + 3 + +zp_len_lo = $ae +zp_len_hi = zp_len_lo + 1 + +zp_bits_lo = $fb +zp_bits_hi = zp_bits_lo + 1 + +zp_dest_hi = $fd +zp_dest_lo = zp_dest_hi + 1 ; dest addr lo +zp_dest_bi = zp_dest_hi + 2 ; dest addr hi + +; ------------------------------------------------------------------- +; symbolic names for constants +; ------------------------------------------------------------------- +buffer_end_hi = buffer_start_hi + buffer_len_hi + +tabl_bi = decrunch_table +tabl_lo = decrunch_table + 52 +tabl_hi = decrunch_table + 104 +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to init the decruncher, it will init used zeropage +; zero page locations and the decrunch tables +; no constraints on register content, however the +; decimal flag has to be #0 (it almost always is, otherwise do a cld) +; ------------------------------------------------------------------- +init_decruncher: + jsr get_crunched_byte + sta zp_bitbuf + + ldx #0 + stx zp_dest_lo + stx zp_dest_hi + stx zp_len_lo + stx zp_len_hi + ldy #0 +; ------------------------------------------------------------------- +; calculate tables (49 bytes) +; x and y must be #0 when entering +; +_init_nextone: + inx + tya + and #$0f + beq _init_shortcut ; starta på ny sekvens + + txa ; this clears reg a + lsr a ; and sets the carry flag + ldx zp_bits_lo +_init_rolle: + rol a + rol zp_bits_hi + dex + bpl _init_rolle ; c = 0 after this (rol zp_bits_hi) + + adc tabl_lo-1,y + tax + + lda zp_bits_hi + adc tabl_hi-1,y +_init_shortcut: + sta tabl_hi,y + txa + sta tabl_lo,y + + ldx #4 + jsr _bit_get_bits ; clears x-reg. + sta tabl_bi,y + iny + cpy #52 + bne _init_nextone +_do_exit: + rts +; ------------------------------------------------------------------- +; decrunch one byte +; +get_decrunched_byte: + ldy zp_len_lo + bne _do_sequence + ldx zp_len_hi + bne _do_sequence2 + + jsr _bit_get_bit1 + beq _get_sequence +; ------------------------------------------------------------------- +; literal handling (13 bytes) +; + jsr get_crunched_byte + bcc _do_literal +; ------------------------------------------------------------------- +; count zero bits + 1 to get length table index (10 bytes) +; y = x = 0 when entering +; +_get_sequence: +_seq_next1: + iny + jsr _bit_get_bit1 + beq _seq_next1 + cpy #$11 + bcs _do_exit +; ------------------------------------------------------------------- +; calulate length of sequence (zp_len) (17 bytes) +; + ldx tabl_bi - 1,y + jsr _bit_get_bits + adc tabl_lo - 1,y + sta zp_len_lo + lda zp_bits_hi + adc tabl_hi - 1,y + sta zp_len_hi +; ------------------------------------------------------------------- +; here we decide what offset table to use (20 bytes) +; x is 0 here +; + bne _seq_nots123 + ldy zp_len_lo + cpy #$04 + bcc _seq_size123 +_seq_nots123: + ldy #$03 +_seq_size123: + ldx tabl_bit - 1,y + jsr _bit_get_bits + adc tabl_off - 1,y + tay +; ------------------------------------------------------------------- +; calulate absolute offset (zp_src) (27 bytes) +; + ldx tabl_bi,y + jsr _bit_get_bits; + adc tabl_lo,y + bcc _seq_skipcarry + inc zp_bits_hi + clc +_seq_skipcarry: + adc zp_dest_lo + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,y + adc zp_dest_hi +; ------------------------------------------------------------------- + cmp #2003,$9e,'2','0','6','1',0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- +.IF DECRUNCH_FORWARDS = 0 + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi +.ELSE + lda $02 + sta _byte_lo + lda $03 + sta _byte_hi +.ENDIF + jmp decrunch +; ------------------------------------------------------------------- +get_crunched_byte: +.IF DECRUNCH_FORWARDS = 0 + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +.ENDIF +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before +.IF DECRUNCH_FORWARDS <> 0 + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: +.ENDIF + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/main1.s b/loader/tools/exomizer-3.1/exodecrs/main1.s new file mode 100644 index 0000000..ab30a52 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/main1.s @@ -0,0 +1,62 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage do the stream decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +.import init_decruncher +.import get_decrunched_byte +.export get_crunched_byte + +.export buffer_start_hi: absolute +.export buffer_len_hi: absolute + + .byte $01,$08,$0b,$08,<2003,>2003,$9e,'2','0','6','1',0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi + jsr init_decruncher +_sample_next: + jsr get_decrunched_byte + bcs _sample_end + ;; do whatever you wish with the value in the accumulator + ldx store_lo + bne skip_store_dec_hi + dec store_hi +skip_store_dec_hi: + dec store_lo +store_lo = * + 1 +store_hi = * + 2 + .byte $8d, 0, 0 ;sta $0000 + + jmp _sample_next +_sample_end: + rts +; ------------------------------------------------------------------- +; for this get_crunched_byte routine to work the crunched data has to be +; crunched using the -m and possibly the -l flags. Any other +; flag will just mess things up. +get_crunched_byte: + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- +buffer_len_hi = 4 ; 1k +unaligned_buffer: + .res (buffer_len_hi * 256) + 255 +buffer_start_hi = (unaligned_buffer + 255) / 256 + diff --git a/loader/tools/exomizer-3.1/exodecrs/main2.s b/loader/tools/exomizer-3.1/exodecrs/main2.s new file mode 100644 index 0000000..1ce2db6 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/main2.s @@ -0,0 +1,75 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage do the stream decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +.import init_decruncher +.import get_decrunched_chunk +.export get_crunched_byte + +.export buffer_start_hi: absolute +.export buffer_len_hi: absolute +.export decrunched_chunk_size: absolute + +decrunched_chunk_size = 128 + + .byte $01,$08,$0b,$08,<2003,>2003,$9e,'2','0','6','1',0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi + jsr init_decruncher +_sample_next: + jsr get_decrunched_chunk + bcs _sample_end + ;; do whatever you wish with the value in the accumulator +.if 1 + lda store_lo + sec + sbc #<(decrunched_chunk_size) + sta store_lo + bcs skip_store_dec_hi + dec store_hi +skip_store_dec_hi: + + ldy #<(decrunched_chunk_size - 1) +_next_byte_in_chunk: + lda ($fe),y +store_lo = * + 1 +store_hi = * + 2 + .byte $99, 0, 0 ;sta $0000 + dey + bpl _next_byte_in_chunk + +.endif + jmp _sample_next +_sample_end: + rts +; ------------------------------------------------------------------- +; for this get_crunched_byte routine to work the crunched data has to be +; crunched using the -m and possibly the -l flags. Any other +; flag will just mess things up. +get_crunched_byte: + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- +buffer_len_hi = 4 ; 1k +unaligned_buffer: + .res (buffer_len_hi * 256) + 255 +buffer_start_hi = (unaligned_buffer + 255) / 256 + diff --git a/loader/tools/exomizer-3.1/exodecrs/perf/Makefile b/loader/tools/exomizer-3.1/exodecrs/perf/Makefile new file mode 100644 index 0000000..889535b --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/perf/Makefile @@ -0,0 +1,176 @@ +# +# Makefile for perf +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s + +#CFLAGS = -g -mtune=i686 $(WFLAGS) +#LDFLAGS = -g -mtune=i686 + +DECR_OBJS = ../exodecrunch.os +DECR_OBJSP+16 = ../exodecrunch.osP+16 +DECR_OBJSM256 = ../exodecrunch.osM256 +DECR_OBJSi = ../exodecrunch.osi +DECR_OBJSiM256 = ../exodecrunch.osiM256 +DECR_OBJSiM256P-32 = ../exodecrunch.osiM256P-32 +CRUNCHER = ../../src/exomizer mem -q $<@0x1010 -o $@ +CRUNCHERP+16 = ../../src/exomizer mem -P+16 -q $<@0x1010 -o $@ +CRUNCHERM256 = ../../src/exomizer mem -M256 -q $<@0x1010 -o $@ +CRUNCHERM256P-32 = ../../src/exomizer mem -P-32 -M256 -q $<@0x1010 -o $@ + +TEST_OBJS = ../testrun.o ../../src/6502emu.o ../../src/exo_util.o ../../src/log.o ../../src/areatrace.o ../../src/vec.o ../../src/buf_io.o ../../src/buf.o ../../src/table.o ../../src/perf.o + +OBJS = ../main.os $(DECR_OBJS) +OBJSP+16 = ../main.os $(DECR_OBJSP+16) +OBJSi = ../main.os $(DECR_OBJSi) +OBJSM256 = ../main.os $(DECR_OBJSM256) +OBJSiM256 = ../main.os $(DECR_OBJSiM256) +OBJSiM256P-32 = ../main.os $(DECR_OBJSiM256P-32) + + +.PHONY: build %.test %.stat +.PRECIOUS: %.cru %.cruM256 %.prg $(TEST_OBJS) %.cru.out %.cruM256.out +#.INTERMEDIATE: %.cru.out + +build: data.test +# +# make .stat shows statistics for all files that match *.raw +# together with average statistics for all files that matched. +# +# use .stat for jsr getbit decruncher +# use .stati for inlined getbit decruncher +# use .statP+16 for a decruncher with separate table for seq len 3 +# use .statM256 for a decruncher with sequence length <= 256 +# use .statiM256 for inlined getbit decruncher with sequence length <= 256 +# +%.stat: test.prg ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.cru,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.cru,$(wildcard $**.raw))), test.prg $(file)) + +%.statP+16: test.prgP+16 ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.cruP+16,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.cruP+16,$(wildcard $**.raw))), test.prgP+16 $(file)) + +%.stati: test.prgi ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.crui,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.crui,$(wildcard $**.raw))), test.prgi $(file)) + +%.statM256: test.prgM256 ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.cruM256,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.cruM256,$(wildcard $**.raw))), test.prgM256 $(file)) + +%.statiM256: test.prgiM256 ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.cruiM256,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.cruiM256,$(wildcard $**.raw))), test.prgiM256 $(file)) + +%.statiM256P-32: test.prgiM256P-32 ../testrun + @$(MAKE) $(sort $(patsubst %.raw,%.cruiM256P-32,$(wildcard $**.raw))) + @../testrun $(foreach file, $(sort $(patsubst %.raw,%.cruiM256P-32,$(wildcard $**.raw))), test.prgiM256P-32 $(file)) + +../testrun: $(TEST_OBJS) + @echo "Linking $@" + @$(CC) $(LDFLAGS) -o $@ $(TEST_OBJS) + +%.test: %.cru.out %.raw + cmp $< $*.raw + +%.testP+16: %.cruP+16.out %.raw + cmp $< $*.raw + +%.testi: %.crui.out %.raw + cmp $< $*.raw + +%.testM256: %.cruM256.out %.raw + cmp $< $*.raw + +%.testiM256: %.cruiM256.out %.raw + cmp $< $*.raw + +%.cru.out: test.prg %.cru ../testrun + ../testrun $< $*.cru + +%.cruP+16.out: test.prgP+16 %.cruP+16 ../testrun + ../testrun $< $*.cruP+16 + +%.cruM256.out: test.prgM256 %.cruM256 ../testrun + ../testrun $< $*.cruM256 + +%.crui.out: testi.prg %.crui ../testrun + ../testrun $< $*.crui + +%.cruiM256.out: test.prgiM256 %.cruiM256 ../testrun + ../testrun $< $*.cruiM256 + +%.cruiM256P-32.out: test.prgiM256P-32 %.cruiM256P-32 ../testrun + ../testrun $< $*.cruiM256P-32 + +%.prg: $(OBJS) ../c64.cfg + ld65 $(OBJS) -o $@ -C../c64.cfg + +%.prgP+16: $(OBJSP+16) ../c64.cfg + ld65 $(OBJSP+16) -o $@ -C../c64.cfg + +%.prgi: $(OBJSi) ../c64.cfg + ld65 $(OBJSi) -o $@ -C../c64.cfg + +%.prgM256: $(OBJSM256) ../c64.cfg + ld65 $(OBJSM256) -o $@ -C../c64.cfg + +%.prgiM256: $(OBJSiM256) ../c64.cfg + ld65 $(OBJSiM256) -o $@ -C../c64.cfg + +%.prgiM256P-32: $(OBJSiM256P-32) ../c64.cfg + ld65 $(OBJSiM256P-32) -o $@ -C../c64.cfg + +%.os: %.s + ca65 $< -o $@ + +%.osP+16: %.s + ca65 -DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1 $< -o $@ + +%.osi: %.s + ca65 -DINLINE_GET_BITS=1 $< -o $@ + +%.osM256: %.s + ca65 -DMAX_SEQUENCE_LENGTH_256=1 $< -o $@ + +%.osiM256: %.s + ca65 -DINLINE_GET_BITS=1 -DMAX_SEQUENCE_LENGTH_256=1 $< -o $@ + +%.osiM256P-32: %.s + ca65 -DINLINE_GET_BITS=1 -DMAX_SEQUENCE_LENGTH_256=1 -DDONT_REUSE_OFFSET=1 $< -o $@ + +clean: + $(RM) *.out *.cru* test*.prg* + +%.cru: %.raw + $(CRUNCHER) + +%.cruP+16: %.raw + $(CRUNCHERP+16) + +%.cruM256: %.raw + $(CRUNCHERM256) + +%.cruM256P-32: %.raw + $(CRUNCHERM256P-32) + +%.crui: %.cru + cp $< $@ + +%.cruiM256: %.cruM256 + cp $< $@ + +%.cruiM256P-32: %.cruM256P-32 + cp $< $@ + +%.o: %.c + @echo "Compiling $<" + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +%: %.o + @$(CC) $(LDFLAGS) $< -o $@ + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/perf/data.raw b/loader/tools/exomizer-3.1/exodecrs/perf/data.raw new file mode 100644 index 0000000..71ccfe8 Binary files /dev/null and b/loader/tools/exomizer-3.1/exodecrs/perf/data.raw differ diff --git a/loader/tools/exomizer-3.1/exodecrs/split/Makefile b/loader/tools/exomizer-3.1/exodecrs/split/Makefile new file mode 100644 index 0000000..a9d5a30 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/split/Makefile @@ -0,0 +1,78 @@ +# +# Makefile for testrun +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = ../../src/6502emu.o ../../src/exo_util.o ../../src/log.o ../../src/areatrace.o ../../src/vec.o ../../src/buf_io.o ../../src/buf.o ../../src/table.o ../../src/perf.o + +permutate2 = $(if $(strip $(2)), $(foreach res, $(call permutate2, $(firstword $(2)), $(wordlist 2, $(words $(2)), $(2))), $(join $(res), _) $(join $(res), $(1))), _ $(1)) + +permutate = $(if $(strip $(1)), $(call permutate2, $(firstword $(1)), $(wordlist 2, $(words $(1)), $(1)))) + +VARIANTS = $(call permutate, i M c 4 o f) + +VARIANT_OPT = c^-DLITERAL_SEQUENCES_NOT_USED=1^-c M^-DMAX_SEQUENCE_LENGTH_256=1^-M256 i^-DINLINE_GET_BITS=1^ o^-DDONT_REUSE_OFFSET=1^-P-32 4^-DEXTRA_TABLE_ENTRY_FOR_LENGTH_THREE=1^-P+16 f^-DDECRUNCH_FORWARDS=1^-f + +echo = $(or $(info $(1) $(2)), $(2)) + +variant = $(suffix $(basename $(1))) + +asmopt = -DENABLE_SPLIT_ENCODING=1 $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 2, $(subst ^, , $(opt)))))) + +exoopt = $(filter-out , $(foreach opt, $(VARIANT_OPT), $(if $(findstring $(word 1, $(subst ^, , $(opt))), $(1)), $(word 3, $(subst ^, , $(opt)))))) + +.PHONY: assert.data% +.PRECIOUS: %.os %.exo %.prg %.out $(TEST_OBJS) +.SECONDEXPANSION: + +build: ../testrun $$(foreach variant, $$(VARIANTS), test.$$(variant).prg data.$$(variant).prg) + @$(RM) *.out + @../testrun $(foreach variant, $(VARIANTS), test.$(variant).prg data.$(variant).prg) + @$(MAKE) $(foreach variant, $(VARIANTS), assert.data.$(variant).prg) + +assert.data%: + cmp -i0:2 data$(call variant, $@).prg.out ../data.bin + @$(RM) data$(call variant, $@).prg.out + +testrun.test%.prg: ../testrun test%.prg data%.prg + @../testrun test$(call variant, $@).prg data$(call variant, $@).prg + @cmp -i0:2 data$(call variant, $@).prg.out ../data.bin && $(RM) data$(call variant, $@).prg.out + +../testrun: ../testrun.o $(TEST_OBJS) + @$(CC) $(LDFLAGS) -o $@ ../testrun.o $(TEST_OBJS) + +test%.prg: main%.os exodecrunch%.os + @echo "building $@" + @ld65 main$(call variant, $@).os exodecrunch$(call variant, $@).os -o $@ -C../c64.cfg + +%.os: %.s + @ca65 $(call asmopt, $(call variant, $@)) $< -o $@ + +exodecrunch%.os: ../$$(basename $$(basename $$@)).s + @ca65 $(call asmopt, $(call variant, $@)) $< -o $@ + +%.os: $$(basename $$(basename $$@)).s + @ca65 $(call asmopt, $(call variant, $@)) $< -o $@ + +clean: + @$(RM) testrun.o *.prg *.out *.os *.exo* testrun + +%.prg: %.os + @ld65 $< -o $@ -C../c64.cfg + +%.exo %.exo.00 %.exo.01: ../$$(basename $$(basename $$@)).bin + @../../src/exomizer mem $(call exoopt, $(call variant, $@)) -q -E $<,0x3000,0,0xb00 $<,0x3b00,0xb00 -o $*.exo + +data%.s: data.s.template data%.exo data%.exo.00 data%.exo.01 + @sed 's/PLACEHOLDER/data$*.exo/' $< > $@ + +%.o: %.c + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +# cancel built in rule that disturb things +%.out: % diff --git a/loader/tools/exomizer-3.1/exodecrs/split/data.s.template b/loader/tools/exomizer-3.1/exodecrs/split/data.s.template new file mode 100644 index 0000000..35c99db --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/split/data.s.template @@ -0,0 +1,26 @@ +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +LOAD_ADDR = $8000 + .word LOAD_ADDR ; prg header +baseLoad: +.IF DECRUNCH_FORWARDS <> 0 + .word tablestart + LOAD_ADDR - baseLoad + .word part0start + LOAD_ADDR - baseLoad + .word part1start + LOAD_ADDR - baseLoad + .word 0 +.ELSE + .word tableend + LOAD_ADDR - baseLoad + .word part0end + LOAD_ADDR - baseLoad + .word part1end + LOAD_ADDR - baseLoad + .word 0 +.ENDIF +tablestart: + .incbin "PLACEHOLDER" +tableend: +part0start: + .incbin "PLACEHOLDER.00" +part0end: +part1start: + .incbin "PLACEHOLDER.01" +part1end: diff --git a/loader/tools/exomizer-3.1/exodecrs/split/main.s b/loader/tools/exomizer-3.1/exodecrs/split/main.s new file mode 100644 index 0000000..4a0b1a3 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/split/main.s @@ -0,0 +1,79 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +; ------------------------------------------------------------------- +.import split_gentable +.import split_decrunch +.export get_crunched_byte + + .byte $01,$08,$0b,$08,<2003,>2003,$9e,'2','0','6','1',0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + lda $02 + sta _ebyte_lo + lda $03 + sta _ebyte_hi + + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr split_gentable + +next_part: + jsr get_Emerge_byte + sta _byte_lo + jsr get_Emerge_byte + cmp #0 + beq done + sta _byte_hi + + jsr split_decrunch + jmp next_part + +done: + rts +; ------------------------------------------------------------------- +get_Emerge_byte: +_ebyte_lo = * + 1 +_ebyte_hi = * + 2 + lda $ffff + inc _ebyte_lo + bne _ebyte_skip_hi + inc _ebyte_hi +_ebyte_skip_hi: + rts +; ------------------------------------------------------------------- +get_crunched_byte: +.IF DECRUNCH_FORWARDS = 0 + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +.ENDIF +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before +.IF DECRUNCH_FORWARDS <> 0 + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: +.ENDIF + rts ; decrunch_file is called. +; ------------------------------------------------------------------- diff --git a/loader/tools/exomizer-3.1/exodecrs/testrun.c b/loader/tools/exomizer-3.1/exodecrs/testrun.c new file mode 100644 index 0000000..e4e5b39 --- /dev/null +++ b/loader/tools/exomizer-3.1/exodecrs/testrun.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2002 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "../src/log.h" +#include "../src/exo_util.h" +#include "../src/6502emu.h" +#include "../src/areatrace.h" +#include "../src/int.h" +#include "../src/buf_io.h" +#include "../src/perf.h" + +#include + +struct mem_ctx +{ + u8 *mem; + struct areatrace at; +}; + +static void mem_access_write(struct mem_access *this, u16 address, u8 value) +{ + struct mem_ctx *ctx = this->ctx; + ctx->mem[address] = value; + areatrace_access(&ctx->at, address); +} + +static u8 mem_access_read(struct mem_access *this, u16 address) +{ + struct mem_ctx *ctx = this->ctx; + return ctx->mem[address]; +} + +void save_single(const char *in_name, struct buf *mem, int start, int end) +{ + struct buf mb_name; + const char *out_name; + + buf_init(&mb_name); + + buf_printf(&mb_name, "%s.out", in_name); + out_name = (const char *)buf_data(&mb_name); + + buf_remove(mem, end, -1); + buf_remove(mem, 0, start); + + write_file(out_name, mem); + + buf_free(&mb_name); +} + +void test_single(const char *prg_name, const char *data_name, + int *cyclesp, int *in_lenp, int *out_lenp) +{ + struct cpu_ctx r; + struct load_info prg_info; + struct load_info data_info; + struct buf mem_mb; + u8 *mem; + struct mem_ctx mem_ctx; + int start; + int end; + + buf_init(&mem_mb); + buf_append(&mem_mb, NULL, 65536); + mem = buf_data(&mem_mb); + memset(mem, 0, 65536); + mem_ctx.mem = mem; + + areatrace_init(&mem_ctx.at); + + prg_info.basic_txt_start = 0x0801; + data_info.basic_txt_start = 0x0801; + load_located(prg_name, mem, &prg_info); + + /* no start address from load*/ + if(prg_info.run == -1) + { + /* look for sys line */ + prg_info.run = find_sys(mem + prg_info.basic_txt_start, 0x9e, NULL); + } + if(prg_info.run == -1) + { + LOG(LOG_ERROR, ("Error, can't find entry point.\n")); + exit(-1); + } + + LOG(LOG_DEBUG, ("run %04x\n", prg_info.run)); + + load_located(data_name, mem, &data_info); + + /* communicate the data region to the prg */ + mem[2] = data_info.start & 255; + mem[3] = data_info.start >> 8; + mem[4] = data_info.end & 255; + mem[5] = data_info.end >> 8; + + r.cycles = 0; + r.mem.ctx = &mem_ctx; + r.mem.read = mem_access_read; + r.mem.write = mem_access_write; + r.pc = prg_info.run; + r.sp = '\xff'; + r.flags = 0; + + /* setting up decrunch */ + while(r.sp >= 0x10) + { + next_inst(&r); + } + + /* save traced area */ + areatrace_merge_overlapping(&mem_ctx.at); + areatrace_get_largest(&mem_ctx.at, &start, &end); + + save_single(data_name, &mem_mb, start, end); + + areatrace_free(&mem_ctx.at); + buf_free(&mem_mb); + + if (cyclesp != NULL) + { + *cyclesp = r.cycles; + } + if (in_lenp != NULL) + { + *in_lenp = data_info.end - data_info.start; + } + if (out_lenp != NULL) + { + *out_lenp = end - start; + } +} + +int main(int argc, char *argv[]) +{ + struct perf_ctx perf; + struct buf buf; + int i; + int cycles; + int inlen; + int outlen; + int cycles_sum = 0; + int inlen_sum = 0; + int outlen_sum = 0; + + /* init logging */ + LOG_INIT_CONSOLE(LOG_TERSE); + + perf_init(&perf); + + for (i = 1; i < argc; i += 2) + { + test_single(argv[i], argv[i + 1], &cycles, &inlen, &outlen); + perf_add(&perf, argv[i + 1], inlen, outlen, cycles); + cycles_sum += cycles; + inlen_sum += inlen; + outlen_sum += outlen; + } + if (argc > 3) + { + perf_add(&perf, "Total", inlen_sum, outlen_sum, cycles_sum); + } + + buf_init(&buf); + perf_buf_print(&perf, &buf); + LOG(LOG_TERSE, ("%s", (char*)buf_data(&buf))); + + buf_free(&buf); + perf_free(&perf); + + return 0; +} diff --git a/loader/tools/exomizer-3.1/rawdecrs/6809/README_exo3.txt b/loader/tools/exomizer-3.1/rawdecrs/6809/README_exo3.txt new file mode 100644 index 0000000..df9b0f8 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/6809/README_exo3.txt @@ -0,0 +1,7 @@ + + Please note that the decrunchers in this folder has not yet been updated to +use the new file format for crunched files that exomizer 3 generates by +default. + + To generate files that are compatible with exomizer 2 you must now add the +flag -P0 to the commandline. diff --git a/loader/tools/exomizer-3.1/rawdecrs/6809/exo2_final.asm b/loader/tools/exomizer-3.1/rawdecrs/6809/exo2_final.asm new file mode 100644 index 0000000..b0a316b --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/6809/exo2_final.asm @@ -0,0 +1,139 @@ +; Exomizer2 algorithm, backward with litterals for 6809 +; by Fool-DupleX, PrehisTO and Sam from PULS (www.pulsdemos.com) +; edouard@forler.ch +; This routine decrunches data compressed with Exomizer2 in raw mode, backward +; with litterals. +; This routine was developed and tested on a Thomson MO5 in July 2011. + +; Please direct any questions about this decruncher to edouard@forler.ch + +; The Exomizer2 decruncher starts here. +; call with a JSR exo2 or equivalent. +; +; Input : U = pointer to last byte of compressed data +; Y = pointer to last byte of output buffer +; Output : Y = pointer to first byte of decompressed data +; Modifies : Y. +; +; All registers are preserved except Y. +; This code self modifies and cannot be run in ROM. +; This code must be contained within a single page (makes use of DP), but may +; be located anywhere in RAM. + +exo2 pshs u,y,x,dp,d,cc ; Save context + tfr pc,d ; Set direct page + tfr a,dp + lda ,u ; Save first byte of data + sta options (without #DEFINE BACKWARD_DECOMPRESS) +;264 bytes - reusable +;258 bytes - single-use +; +;compress with options (with #DEFINE BACKWARD_DECOMPRESS) +;259 bytes - reusable +;253 bytes - single-use +; +;Compile with The Telemark Assembler (TASM) 3.2 + +exo_mapbasebits .equ 0 + + +;#DEFINE BACKWARD_DECOMPRESS +;#DEFINE REUSABLE + +#IFNDEF BACKWARD_DECOMPRESS + +.DEFINE NEXT_HL inx h +.DEFINE NEXT_DE inx d +.DEFINE ADD_OFFSET mov a,e\ sub l\ mov l,a\ mov a,d\ sbb h\ mov h,a + +#ELSE + +.DEFINE NEXT_HL dcx h +.DEFINE NEXT_DE dcx d +.DEFINE ADD_OFFSET dad d + +#ENDIF + + +deexo: + +#IFDEF REUSABLE + mvi a,80h + sta exo_getbit+1 +#ENDIF + + push d + xra a + mov c,a +exo_initbits: + sui 48 + jnc exo_initbits + adi 48 + jnz exo_node1 + lxi d,1 +exo_node1: + mvi b,4 + push d + call exo_getbits_ + xra a + ora e + pop d + rrc + push h + mvi b,exo_mapbasebits>>8 + stax b + jnc skipPlus8 + adi -128+8 +skipPlus8: + lxi h,1 + jz skip_exo_setbit +exo_setbit: + dad h + dcr a + jnz exo_setbit +skip_exo_setbit: + dad d + inr c + mov a,e + stax b + inr c + mov a,d + stax b + inr c + xchg + pop h + mov a,c + cpi 52*3 + jnz exo_initbits + pop d + xra a + mov b,a + mov c,a +#IFDEF REUSABLE + inr a +#ENDIF + jmp exo_mainloop +not_reuse_offset: + push d + lxi b,0230h + dcx d + mov a,d + ora e + jz exo_goforit + lxi b,0420h + dcx d + mov a,d + ora e + jz exo_goforit +exo_goforit_: + mvi c,10h +exo_goforit: + call exo_getbits_ + + mov a,c + add e + mov c,a + call exo_getpair + pop b + xthl + xchg + shld reuse_offset+1 +reuse_offset_: + ADD_OFFSET + call ldir + pop h +exo_mainloop: + sta reuse_offset_state+1 + call exo_getbit_InrC + jc exo_literalcopy + mvi c,0FFh +exo_getindex: + call exo_getbit_InrC + jnc exo_getindex + mov a,c + cpi 16 + jc exo_continue + rz + mov b,m + NEXT_HL + mov c,m + NEXT_HL +exo_literalcopy: + call ldir +reuse_offset_state: + mvi a,1 + stc + adc a + jmp exo_mainloop +exo_continue: + push d + call exo_getpair + lda reuse_offset_state+1 + ani 3 + jpe not_reuse_offset + call exo_getbit + jnc not_reuse_offset + mov c,e + mov b,d + pop d + push h +reuse_offset: + lxi h,0 + jmp reuse_offset_ +exo_getpair: + add a + add c + mov c,a + mvi b,exo_mapbasebits>>8 + ldax b + mov b,a + mov e,a + mov d,a + ora a + cnz exo_getbits + inr c + mvi b,exo_mapbasebits>>8 + ldax b + inr c + add e + mov e,a + ldax b + adc d + mov d,a + ret + +exo_getbits: + jm GT7 +exo_getbits_: + mvi e,0 +exo_gettingbits: + call exo_getbit + mov a,e\ adc a\ mov e,a + dcr b + jnz exo_gettingbits + mov d,b + ret +GT7: + ani 7Fh + mov b,a + mov e,a + cnz exo_getbits_ + mov d,e + mov e,m + NEXT_HL + ret + +exo_getbit_InrC: + inr c +exo_getbit: + mvi a,80h + add a + sta exo_getbit+1 + rnz + mov a,m + NEXT_HL + adc a + jmp $-7 + +ldir: + mov a,m + stax d + NEXT_HL + NEXT_DE + dcx b + mov a,b + ora c + jnz $-7 + ret + + .end \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/8080/P43E/deexo.asm b/loader/tools/exomizer-3.1/rawdecrs/8080/P43E/deexo.asm new file mode 100644 index 0000000..d58de94 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/8080/P43E/deexo.asm @@ -0,0 +1,249 @@ +;Exomizer3 Intel 8080 decoder version by Ivan Gorodetsky +;Based on Exomizer2 Z80 decoder by Metalbrain, but now it is nearly completely rewritten +;Compression algorithm by Magnus Lind +; +;1. Initialisation +;input: hl=compressed table start +; call deexo_MakeTable +;2. Decompression +;input: hl=compressed data start +; de=uncompressed destination start +; you may change exo_mapbasebits to point to any 256-byte aligned free buffer +; call deexo +; +;v1.1 - 2019-10-08 +;compress with options (without #DEFINE BACKWARD_DECOMPRESS) +;279 bytes +; +;compress with options (with #DEFINE BACKWARD_DECOMPRESS) +;274 bytes +; +;Compile with The Telemark Assembler (TASM) 3.2 + +exo_mapbasebits .equ 0 + +;#DEFINE BACKWARD_DECOMPRESS + +.include "deexohead.asm" + +#IFNDEF BACKWARD_DECOMPRESS + +.DEFINE NEXT_HL inx h +.DEFINE NEXT_DE inx d +.DEFINE ADD_OFFSET mov a,e\ sub l\ mov l,a\ mov a,d\ sbb h\ mov h,a + +#ELSE + +.DEFINE NEXT_HL dcx h +.DEFINE NEXT_DE dcx d +.DEFINE ADD_OFFSET dad d + +#ENDIF + + +deexo_MakeTable: + xra a + mov c,a +exo_initbits: + sui 48 + jnc exo_initbits + adi 48 + jnz exo_node1 + lxi d,1 +exo_node1: + rrc + mov a,m + jc LoNib + rrc + rrc + rrc + rrc + .db 0FEh +LoNib: + NEXT_HL + ani 0Fh + + rrc + push h + mvi b,exo_mapbasebits>>8 + stax b + jnc skipPlus8 + adi -128+8 +skipPlus8: + lxi h,1 + jz skip_exo_setbit +exo_setbit: + dad h + dcr a + jnz exo_setbit +skip_exo_setbit: + dad d + inr c + mov a,e + stax b + inr c + mov a,d + stax b + inr c + xchg + pop h + mov a,c + cpi 52*3 + jnz exo_initbits + ret + +deexo: + mvi a,80h + sta exo_getbit+1 + xra a + mov b,a + mov c,a + inr a + jmp exo_mainloop +not_reuse_offset: + push d + lxi b,0230h + dcx d + mov a,d + ora e + jz exo_goforit + lxi b,0420h + dcx d + mov a,d + ora e + jz exo_goforit +exo_goforit_: + mvi c,10h +exo_goforit: + call exo_getbits_ + + mov a,c + add e + mov c,a + call exo_getpair + pop b + xthl + xchg + shld reuse_offset+1 +reuse_offset_: + ADD_OFFSET + call ldir + pop h +exo_mainloop: + sta reuse_offset_state+1 + call exo_getbit_InrC + jc exo_literalcopy + mvi c,0FFh +exo_getindex: + call exo_getbit_InrC + jnc exo_getindex + mov a,c + cpi 16 + jc exo_continue + rz + mov b,m + NEXT_HL + mov c,m + NEXT_HL +exo_literalcopy: + call ldir +reuse_offset_state: + mvi a,1 + stc + adc a + jmp exo_mainloop +exo_continue: + push d + call exo_getpair + lda reuse_offset_state+1 + ani 3 + jpe not_reuse_offset + call exo_getbit_ + jnc not_reuse_offset + mov c,e + mov b,d + pop d + push h +reuse_offset: + lxi h,0 + jmp reuse_offset_ + +exo_getpair: + add a + add c + mov c,a + mvi b,exo_mapbasebits>>8 + ldax b + mov b,a + mov e,a + mov d,a + ora a + cnz exo_getbits + + inr c + mvi b,exo_mapbasebits>>8 + ldax b + inr c + add e + mov e,a + ldax b + adc d + mov d,a + ret + +exo_getbits: + jm GT7 +exo_getbits_: + xchg +exo_getbit: + lxi h,0080h + mov a,l +exo_gettingbits: + add a + jnz $+7 \ ldax d\ NEXT_DE\ mov l,a\ adc a + dad h + dcr b + jnz exo_gettingbits +exo_gettingbitsexit: + xchg + sta exo_getbit+1 + mov e,d + mov d,b + ret +GT7: + ani 7Fh + mov b,a + mov e,a + cnz exo_getbits_ + mov d,e + mov e,m + NEXT_HL + ret + +exo_getbit_InrC: + inr c +exo_getbit_: + lda exo_getbit+1 + add a + sta exo_getbit+1 + rnz + mov a,m + NEXT_HL + adc a + jmp $-7 + +ldir: + mov a,m + stax d + NEXT_HL + NEXT_DE + dcx b + mov a,b + ora c + jnz $-7 + ret + +packed: + + + .end \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/8080/P47T4/deexo.asm b/loader/tools/exomizer-3.1/rawdecrs/8080/P47T4/deexo.asm new file mode 100644 index 0000000..956a0b6 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/8080/P47T4/deexo.asm @@ -0,0 +1,233 @@ +;Exomizer3 Intel 8080 decoder version by Ivan Gorodetsky +;Based on Exomizer2 Z80 decoder by Metalbrain, but now it is nearly completely rewritten +;Compression algorithm by Magnus Lind +;input: hl=compressed data start +; de=uncompressed destination start +; you may change exo_mapbasebits to point to any 256-byte aligned free buffer +; +;v1.1 - 2019-10-28 +; +;compress with options (without #DEFINE BACKWARD_DECOMPRESS) +;294 bytes - reusable +;285 bytes - single-use +;compress with options (with #DEFINE BACKWARD_DECOMPRESS) +;289 bytes - reusable +;280 bytes - single-use +; +;Compile with The Telemark Assembler (TASM) 3.2 + +exo_mapbasebits .equ 0FF00h + +;#DEFINE BACKWARD_DECOMPRESS +;#DEFINE REUSABLE + +#IFNDEF BACKWARD_DECOMPRESS + +.DEFINE NEXT_HL inx h +.DEFINE NEXT_DE inx d +.DEFINE ADD_OFFSET mov a,e\ sub l\ mov l,a\ mov a,d\ sbb h\ mov h,a + +#ELSE + +.DEFINE NEXT_HL dcx h +.DEFINE NEXT_DE dcx d +.DEFINE ADD_OFFSET dad d + +#ENDIF + + .org 8000h + +deexo: +#IFDEF REUSABLE + mvi a,80h + sta exo_getbit+1 + rlc + sta reuse_offset_state+1 +#ENDIF + + push d + xra a + mov c,a +exo_initbits: + sui 48 + jnc exo_initbits + adi 48 + jnz exo_node1 + lxi d,1 +exo_node1: + mvi b,4 + push d + call exo_getbits_ + xra a + ora e + pop d + rrc + push h + mvi b,exo_mapbasebits>>8 + stax b + jnc skipPlus8 + adi -128+8 +skipPlus8: + lxi h,1 + jz skip_exo_setbit +exo_setbit: + dad h + dcr a + jnz exo_setbit +skip_exo_setbit: + dad d + inr c + mov a,e + stax b + inr c + mov a,d + stax b + inr c + xchg + pop h + mov a,c + cpi 52*3 + jnz exo_initbits + pop d + lxi b,0 +exo_literalcopy1: + mov a,m\ stax d\ NEXT_HL\ NEXT_DE +exo_mainloop_: + stc +reuse_offset_state: + mvi a,1 + adc a +exo_mainloop: + sta reuse_offset_state+1 + lda exo_getbit+1 + add a + jnz $+6\ mov a,m\ NEXT_HL\ adc a + sta exo_getbit+1 + jc exo_literalcopy1 + dcr c +exo_getindex: + inr c + add a + jnz $+6\ mov a,m\ NEXT_HL\ adc a + jnc exo_getindex + sta exo_getbit+1 + mov a,c + cpi 16 + jc exo_continue + rz + mov b,m + NEXT_HL + mov c,m + NEXT_HL + mov a,m + stax d + NEXT_HL + NEXT_DE + dcx b + mov a,b + ora c + jnz $-7 + jmp exo_mainloop_ +exo_continue: + push d + call exo_getpair + lda reuse_offset_state+1 + ani 3 + jpe not_reuse_offset + lda exo_getbit+1 + add a + jnz $+6\ mov a,m\ NEXT_HL\ adc a + sta exo_getbit+1 + jnc not_reuse_offset + mov c,e + mov b,d + pop d + push h +reuse_offset: + lxi h,0 + jmp exo_matchcopy +not_reuse_offset: + push d + lxi b,0230h + dcr e + jz exo_goforit + lxi b,0420h + dcr e + jz exo_goforit +exo_goforit_: + mvi c,10h +exo_goforit: + call exo_getbits_ + mov a,c + add e + mov c,a + call exo_getpair + pop b + xthl + xchg + shld reuse_offset+1 +exo_matchcopy: + ADD_OFFSET + mov a,m + stax d + NEXT_HL + NEXT_DE + dcx b + mov a,b + ora c + jnz $-7 + pop h + jmp exo_mainloop +exo_getpair: + add a + add c + mov c,a + mvi b,exo_mapbasebits>>8 + ldax b + mov b,a + mov e,a + mov d,a + ora a + cnz exo_getbits + inr c + mvi b,exo_mapbasebits>>8 + ldax b + inr c + add e + mov e,a + ldax b + adc d + mov d,a + ret + +exo_getbits: + jm GT7 +exo_getbits_: + xchg +exo_getbit: + lxi h,0080h + mov a,l +exo_gettingbits: + add a + jnz $+7 \ ldax d\ NEXT_DE\ mov l,a\ adc a + dad h + dcr b + jnz exo_gettingbits +exo_gettingbitsexit: + xchg + sta exo_getbit+1 + mov e,d + mov d,b + ret +GT7: + ani 7Fh + mov b,a + mov e,a + cnz exo_getbits_ + mov d,e + mov e,m + NEXT_HL + ret + + + .end \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/8080/README.txt b/loader/tools/exomizer-3.1/rawdecrs/8080/README.txt new file mode 100644 index 0000000..2ee3e92 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/8080/README.txt @@ -0,0 +1,7 @@ +This directory contains exomizer decrunchers in assembly for Intel 8080 contributed by Ivan Gorodetsky (retrocomp@yandex.ru). +Compile with The Telemark Assembler (TASM) 3.2 + +P43 - size-optimized version +P47T4 - speed-optimized version + +P43E - support of shared header/table info for multiple crunched files \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/8086/P47/deexo.asm b/loader/tools/exomizer-3.1/rawdecrs/8086/P47/deexo.asm new file mode 100644 index 0000000..e45290c --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/8086/P47/deexo.asm @@ -0,0 +1,173 @@ +;Exomizer3 Intel 8088/86 decoder version by Ivan Gorodetsky +;Compression algorithm by Magnus Lind +; +;Memory model - Tiny +;input: si=compressed data start +; di=uncompressed destination start +; you may change exo_mapbasebits to point to any 256-byte aligned free buffer +; +;v1.1 - 2019-10-28 +; +;compress with options (with BACKWARD equ 0) +;compress with options (with BACKWARD equ 1) +;212 bytes +; +;Compile with flat assembler (FASM) + + +BACKWARD equ 0 + +macro SetDir { + if BACKWARD eq 1 + std + else + cld + end if +} + +macro NextSI { + if BACKWARD eq 1 + dec si + else + inc si + end if +} + +macro AddOffset { + if BACKWARD eq 1 + add si,bp + else + sub si,bp + end if +} + +macro GetBit { + add al,al + jnz $+5 + lodsb + adc al,al +} + +exo_table equ 0 + +deexo: + mov ax,0180h + mov bx,exo_table + xor ch,ch + SetDir + push di +exo_initable: + test bl,63 + jnz exo_node1 + mov di,1 +exo_node1: + mov cl,4 + call exo_getbits + mov cl,dl + ror cl,1 + mov [bx],cl + jnc exo_skipPlus8 + add cl,-128+8 +exo_skipPlus8: + mov bp,1 + shl bp,cl + inc bx + inc bx + mov [bx],di + inc bx + inc bx + add di,bp + cmp bl,52*4 + jnz exo_initable + pop di + xor cx,cx +exo_literalcopy1: + movsb + stc +exo_mainloop: + adc ah,ah + GetBit + jc exo_literalcopy1 + dec cx +exo_getindex: + inc cx + GetBit + jnc exo_getindex + cmp cl,16 + jc exo_continue + jz exo_ret + mov ch,[si] + NextSI + mov cl,[si] + NextSI +exo_literalcopy: + rep movsb + stc + jmp exo_mainloop +exo_continue: + mov bl,cl + call exo_getpair + mov cl,ah + and cl,3 + dec cl + jnz not_reuse_offset + GetBit + jnc not_reuse_offset + mov cx,dx + jmp reuse_offset +not_reuse_offset: + push dx + mov cl,02h + mov bl,30h + dec dx + jz exo_gogetbits + mov cl,04h + mov bl,20h + dec dx + jz exo_gogetbits + mov bl,10h +exo_gogetbits: + call exo_getbits + add bl,dl + call exo_getpair + mov bp,dx + pop cx +reuse_offset: + mov dx,si + mov si,di + AddOffset + rep movsb + mov si,dx + jmp exo_mainloop + +exo_getpair: + add bl,bl + add bl,bl + mov cl,[bx] + call exo_getbits + inc bx + inc bx + add dx,[bx] + ret + +exo_getbits: + xor dx,dx + test cl,cl + jz exo_ret + jns exo_gettingbits + and cl,7Fh + jz GT7z + call exo_gettingbits + mov dh,dl +GT7z: + mov dl,[si] + NextSI + ret +exo_gettingbits: + GetBit + adc dx,dx + loop exo_gettingbits +exo_ret: + ret + + diff --git a/loader/tools/exomizer-3.1/rawdecrs/8086/README.txt b/loader/tools/exomizer-3.1/rawdecrs/8086/README.txt new file mode 100644 index 0000000..d82349c --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/8086/README.txt @@ -0,0 +1,2 @@ +This directory contains exomizer decrunchers in assembly for Intel 8088/86 contributed by Ivan Gorodetsky (retrocomp@yandex.ru). +Compile with flat assembler (FASM) diff --git a/loader/tools/exomizer-3.1/rawdecrs/Makefile b/loader/tools/exomizer-3.1/rawdecrs/Makefile new file mode 100644 index 0000000..3a234dc --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/Makefile @@ -0,0 +1,61 @@ +# +# Makefile for exomizer decruncher +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +TEST_OBJS = main.o exodecrunch.o +TEST2_OBJS = main2.o exodecr.o + +#.SILENT: + +.PHONY: build + +build: $(MAKEFILE) test test2 + + + +test: main test1.exo test2.exo test3.exo test4.exo + ./main test1.exo test1.dec + cmp test1.bin test1.dec + ./main test2.exo test2.dec + cmp test2.bin test2.dec + ./main test3.exo test3.dec + cmp test3.bin test3.dec + ./main test4.exo test4.dec + cmp test4.bin test4.dec + +test2: main2 test1.oxe test2.oxe test3.oxe test4.oxe + ./main2 test1.oxe test1.dec + cmp test1.bin test1.dec + ./main2 test2.oxe test2.dec + cmp test2.bin test2.dec + ./main2 test3.oxe test3.dec + cmp test3.bin test3.dec + ./main2 test4.oxe test4.dec + cmp test4.bin test4.dec + +main: deps $(TEST_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TEST_OBJS) + +main2: deps $(TEST2_OBJS) + $(CC) $(LDFLAGS) -o $@ $(TEST2_OBJS) + +clean: + -$(RM) $(TEST_OBJS) $(TEST2_OBJS) main main.exe main2 main2.exe \ + *.o deps *.exo *.oxe *.dec + +-include deps + +deps: $(wildcard *.h) + $(CC) -MM $(wildcard *.c) >$@ + +%.exo: %.bin + ../src/exomizer raw -q -C $< -o $@ + +%.oxe: %.bin + ../src/exomizer raw -q -C -b $< -o $@ diff --git a/loader/tools/exomizer-3.1/rawdecrs/exodecr.c b/loader/tools/exomizer-3.1/rawdecrs/exodecr.c new file mode 100644 index 0000000..1689f78 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/exodecr.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2005-2017 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented * you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + */ + +/** + * This decompressor decompresses files that have been compressed + * using the raw sub-sub command with the -b (not default) and -P39 + * (default) setting of the raw command. + */ +#include "exodecr.h" + +static unsigned short int base[52]; +static char bits[52]; +static unsigned char bit_buffer; + +static int bitbuffer_rotate(int carry) +{ + int carry_out; + /* rol */ + carry_out = (bit_buffer & 0x80) != 0; + bit_buffer <<= 1; + if (carry) + { + bit_buffer |= 0x01; + } + return carry_out; +} + +static unsigned char read_byte(const char **inp) +{ + unsigned char val = *--(*inp) & 0xff; + return val; +} + +static unsigned short int +read_bits(const char **inp, int bit_count) +{ + unsigned short int bits = 0; + int byte_copy = bit_count & 8; + bit_count &= 7; + + while(bit_count-- > 0) + { + int carry = bitbuffer_rotate(0); + if (bit_buffer == 0) + { + bit_buffer = read_byte(inp); + carry = bitbuffer_rotate(1); + } + bits <<= 1; + bits |= carry; + } + if (byte_copy != 0) + { + bits <<= 8; + bits |= read_byte(inp); + } + return bits; +} + +static void +init_table(const char **inp) +{ + int i; + unsigned short int b2; + + for(i = 0; i < 52; ++i) + { + unsigned short int b1; + if((i & 15) == 0) + { + b2 = 1; + } + base[i] = b2; + + b1 = read_bits(inp, 3); + b1 |= read_bits(inp, 1) << 3; + bits[i] = b1; + + b2 += 1 << b1; + } +} + +char * +exo_decrunch(const char *in, char *out) +{ + unsigned short int index; + unsigned short int length; + unsigned short int offset; + char c; + char literal = 1; + char reuse_offset_state = 1; + + bit_buffer = read_byte(&in); + + init_table(&in); + + goto implicit_literal_byte; + for(;;) + { + literal = read_bits(&in, 1); + if(literal == 1) + { + implicit_literal_byte: + /* literal byte */ + length = 1; + goto copy; + } + index = 0; + while(read_bits(&in, 1) == 0) + { + ++index; + } + if(index == 16) + { + break; + } + if(index == 17) + { + literal = 1; + length = read_byte(&in) << 8; + length |= read_byte(&in); + goto copy; + } + length = base[index]; + length += read_bits(&in, bits[index]); + + if ((reuse_offset_state & 3) != 1 || !read_bits(&in, 1)) + { + switch(length) + { + case 1: + index = read_bits(&in, 2); + index += 48; + break; + case 2: + index = read_bits(&in, 4); + index += 32; + break; + default: + index = read_bits(&in, 4); + index += 16; + break; + } + offset = base[index]; + offset += read_bits(&in, bits[index]); + } + copy: + do + { + --out; + if(literal) + { + c = read_byte(&in); + } + else + { + c = out[offset]; + } + *out = c; + } + while(--length > 0); + + reuse_offset_state = (reuse_offset_state << 1) | literal; + } + return out; +} diff --git a/loader/tools/exomizer-3.1/rawdecrs/exodecr.h b/loader/tools/exomizer-3.1/rawdecrs/exodecr.h new file mode 100644 index 0000000..6840ca9 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/exodecr.h @@ -0,0 +1,37 @@ +#ifndef EXO_DECR_ALREADY_INCLUDED +#define EXO_DECR_ALREADY_INCLUDED + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented * you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + */ + +/** + * This decompressor decompresses files that have been compressed + * using the raw sub-sub command with the -b (not default) and -P39 + * (default) setting of the raw command. + */ +char *exo_decrunch(const char *in, char *out); + +#endif /* EXO_DECRUNCH_ALREADY_INCLUDED */ diff --git a/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.c b/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.c new file mode 100644 index 0000000..0fd38a1 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2005-2017 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented * you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + */ + +/** + * This decompressor decompresses files that have been compressed + * using the raw sub-sub command with the -P39 (default) setting of + * the raw command. + */ +#include "exodecrunch.h" +#include + +enum exo_state +{ + STATE_IMPLICIT_FIRST_LITERAL_BYTE, + STATE_NEXT_BYTE, + STATE_NEXT_LITERAL_BYTE, + STATE_NEXT_SEQUENCE_BYTE, + STATE_EOF +}; + +struct exo_table_entry +{ + unsigned char bits; + unsigned short int base; +}; + +struct exo_decrunch_ctx +{ + struct exo_table_entry *lengths; + struct exo_table_entry *offsets1; + struct exo_table_entry *offsets2; + struct exo_table_entry *offsets3; + unsigned char *window; + unsigned short int window_length; + + enum exo_state state; + unsigned short int length; + unsigned short int offset; + unsigned short int window_pos; + unsigned char bit_buffer; + unsigned char reuse_offset_state; + + exo_read_crunched_byte *read_byte; + void *read_data; +}; + +static int bitbuffer_rotate(struct exo_decrunch_ctx *ctx, int carry) +{ + int carry_out; + /* rol */ + carry_out = (ctx->bit_buffer & 0x80) != 0; + ctx->bit_buffer <<= 1; + if (carry) + { + ctx->bit_buffer |= 0x01; + } + return carry_out; +} + +static int +read_bits(struct exo_decrunch_ctx *ctx, int bit_count) +{ + int byte_copy = bit_count & 8; + int bits = 0; + bit_count &= 7; + while(bit_count-- > 0) + { + int carry = bitbuffer_rotate(ctx, 0); + if(ctx->bit_buffer == 0) + { + ctx->bit_buffer = ctx->read_byte(ctx->read_data); + carry = bitbuffer_rotate(ctx, 1); + } + bits <<= 1; + bits |= carry; + } + if (byte_copy != 0) + { + bits <<= 8; + bits |= ctx->read_byte(ctx->read_data); + } + return bits; +} + +static struct exo_table_entry * +generate_table(struct exo_decrunch_ctx *ctx, int size) +{ + int base; + int i; + struct exo_table_entry *table; + table = malloc(size * sizeof(struct exo_table_entry)); + base = 1; + for(i = 0; i < size; ++i) + { + table[i].base = base; + table[i].bits = (unsigned char)read_bits(ctx, 3); + table[i].bits |= (unsigned char)read_bits(ctx, 1) << 3; + base += 1 << table[i].bits; + } + return table; +} + +struct exo_decrunch_ctx * +exo_decrunch_new(unsigned short int max_offset, + exo_read_crunched_byte *read_byte, + void *read_data) +{ + struct exo_decrunch_ctx *ctx; + + ctx = malloc(sizeof(struct exo_decrunch_ctx)); + + ctx->state = STATE_IMPLICIT_FIRST_LITERAL_BYTE; + ctx->window_pos = 0; + ctx->bit_buffer = read_byte(read_data); + ctx->reuse_offset_state = 1; + ctx->read_byte = read_byte; + ctx->read_data = read_data; + + ctx->lengths = generate_table(ctx, 16); + ctx->offsets3 = generate_table(ctx, 16); + ctx->offsets2 = generate_table(ctx, 16); + ctx->offsets1 = generate_table(ctx, 4); + ctx->window = malloc(max_offset); + ctx->window_length = max_offset; + + return ctx; +} + +static int +get_gamma_code(struct exo_decrunch_ctx *ctx) +{ + int gamma; + + gamma = 0; + while(read_bits(ctx, 1) == 0) + { + ++gamma; + } + return gamma; +} + +static +int read_byte_from_window(struct exo_decrunch_ctx *ctx, int offset) +{ + int read_pos; + + read_pos = ctx->window_pos - offset; + if(read_pos < 0) + { + read_pos += ctx->window_length; + } + return ctx->window[read_pos]; +} + +int +exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx) +{ + int c; + int length_index; + struct exo_table_entry *table_entry; + + switch(ctx->state) + { + case STATE_IMPLICIT_FIRST_LITERAL_BYTE: + ctx->state = STATE_NEXT_BYTE; + goto implicit_literal_byte; + case STATE_NEXT_BYTE: + if(read_bits(ctx, 1) == 1) + { + implicit_literal_byte: + /* literal byte */ + c = ctx->read_byte(ctx->read_data); + ctx->reuse_offset_state += ctx->reuse_offset_state + 1; + break; + } + /* sequence */ + length_index = get_gamma_code(ctx); + if(length_index == 17) + { + /* literal data block */ + ctx->length = ctx->read_byte(ctx->read_data) << 8; + ctx->length |= ctx->read_byte(ctx->read_data); + ctx->state = STATE_NEXT_LITERAL_BYTE; + case STATE_NEXT_LITERAL_BYTE: + if(--ctx->length == 0) + { + ctx->state = STATE_NEXT_BYTE; + ctx->reuse_offset_state += ctx->reuse_offset_state + 1; + } + c = ctx->read_byte(ctx->read_data); + break; + } + else if(length_index == 16) + { + /* end of data marker, we're done. */ + ctx->state = STATE_EOF; + case STATE_EOF: + default: + return -1; + } + /* sequence */ + table_entry = ctx->lengths + length_index; + ctx->length = table_entry->base + read_bits(ctx, table_entry->bits); + + if ((ctx->reuse_offset_state & 3) != 1 || !read_bits(ctx, 1)) + { + switch(ctx->length) + { + case 1: + table_entry = ctx->offsets1 + read_bits(ctx, 2); + break; + case 2: + table_entry = ctx->offsets2 + read_bits(ctx, 4); + break; + default: + table_entry = ctx->offsets3 + read_bits(ctx, 4); + } + ctx->offset = table_entry->base + read_bits(ctx, table_entry->bits); + } + ctx->state = STATE_NEXT_SEQUENCE_BYTE; + case STATE_NEXT_SEQUENCE_BYTE: + if(--ctx->length == 0) + { + ctx->state = STATE_NEXT_BYTE; + ctx->reuse_offset_state += ctx->reuse_offset_state + 0; + } + c = read_byte_from_window(ctx, ctx->offset); + break; + } + + ctx->window[ctx->window_pos++] = (unsigned char)c; + if(ctx->window_pos == ctx->window_length) + { + ctx->window_pos = 0; + } + return c; +} + +void +exo_decrunch_delete(struct exo_decrunch_ctx *ctx) +{ + free(ctx->window); + free(ctx->offsets1); + free(ctx->offsets2); + free(ctx->offsets3); + free(ctx->lengths); + free(ctx); +} diff --git a/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.h b/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.h new file mode 100644 index 0000000..b165eda --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/exodecrunch.h @@ -0,0 +1,50 @@ +#ifndef EXO_DECRUNCH_ALREADY_INCLUDED +#define EXO_DECRUNCH_ALREADY_INCLUDED + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented * you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + */ + +/** + * This decompressor decompresses files that have been compressed + * using the raw sub-sub command with the -P39 (default) setting of + * the raw command. + */ +#define MAX_OFFSET 65535 + +typedef int exo_read_crunched_byte(void *data); + +struct exo_decrunch_ctx * +exo_decrunch_new(unsigned short int max_offset, + exo_read_crunched_byte *read_byte, + void *read_data); + +int +exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx); + +void +exo_decrunch_delete(struct exo_decrunch_ctx *ctx); + +#endif /* EXO_DECRUNCH_ALREADY_INCLUDED */ diff --git a/loader/tools/exomizer-3.1/rawdecrs/main.c b/loader/tools/exomizer-3.1/rawdecrs/main.c new file mode 100644 index 0000000..36fc76a --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/main.c @@ -0,0 +1,55 @@ + +#include "exodecrunch.h" +#include +#include + +static int read_byte(void *read_data) +{ + FILE *in; + int c; + + in = (FILE*)read_data; + c = fgetc(in); + + return c; +} + +int main(int argc, char *argv[]) +{ + FILE *in; + FILE *out; + struct exo_decrunch_ctx *ctx; + int c; + + if(argc != 3) + { + fprintf(stderr, "Error: usage: %s \n", argv[0]); + exit(-1); + } + in = fopen(argv[1], "rb"); + if(in == NULL) + { + fprintf(stderr, "Error: can't open %s for input.\n", argv[1]); + exit(-1); + } + out = fopen(argv[2], "wb"); + if(in == NULL) + { + fprintf(stderr, "Error: can't open %s for output.\n", argv[2]); + exit(-1); + } + + ctx = exo_decrunch_new(MAX_OFFSET, read_byte, in); + + while((c = exo_read_decrunched_byte(ctx)) != EOF) + { + fputc(c, out); + } + + exo_decrunch_delete(ctx); + + fclose(out); + fclose(in); + + return 0; +} diff --git a/loader/tools/exomizer-3.1/rawdecrs/main2.c b/loader/tools/exomizer-3.1/rawdecrs/main2.c new file mode 100644 index 0000000..a0221ba --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/main2.c @@ -0,0 +1,48 @@ + +#include "exodecr.h" +#include +#include + +int main(int argc, char *argv[]) +{ + FILE *in; + FILE *out; + char *inp; + char *outp; + char *p; + int len; + + if(argc != 3) + { + fprintf(stderr, "Error: usage: %s \n", argv[0]); + exit(-1); + } + in = fopen(argv[1], "rb"); + if(in == NULL) + { + fprintf(stderr, "Error: can't open %s for input.\n", argv[1]); + exit(-1); + } + out = fopen(argv[2], "wb"); + if(in == NULL) + { + fprintf(stderr, "Error: can't open %s for output.\n", argv[2]); + exit(-1); + } + + p = malloc(500000); + len = fread(p, 1, 500000, in); + inp = p + len; + outp = p + 500000; + + outp = exo_decrunch(inp, outp); + + fwrite(outp, 1, p + 500000 - outp, out); + + free(p); + + fclose(out); + fclose(in); + + return 0; +} diff --git a/loader/tools/exomizer-3.1/rawdecrs/test1.bin b/loader/tools/exomizer-3.1/rawdecrs/test1.bin new file mode 100644 index 0000000..f87cacf Binary files /dev/null and b/loader/tools/exomizer-3.1/rawdecrs/test1.bin differ diff --git a/loader/tools/exomizer-3.1/rawdecrs/test2.bin b/loader/tools/exomizer-3.1/rawdecrs/test2.bin new file mode 100644 index 0000000..5118e70 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/test2.bin @@ -0,0 +1 @@ +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/test3.bin b/loader/tools/exomizer-3.1/rawdecrs/test3.bin new file mode 100644 index 0000000..d358842 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/test3.bin @@ -0,0 +1 @@ +%A Abdou, I.E. %A Wong, K.Y. %D 1982 %T Analysis of linear interpolation schemes for bi-level image applications %J IBM J Research and Development %V 26 %P 667-680 %A Abell, R. %D 1981 %T Implementation of a Telidon system using Unix file structures %B The Telidon book %E D.Godfrey and E.Chang %I Press Porcepic %C Toronto, ON %P 203-209 %A Abut, H. %A Gray, R.M. %A Rebolledo, V. %D 1982 %T Vector quantization of speech and speech-like waveforms %J IEEE Trans Acoustics, Speech and Signal Processing %V Acoustics, Speech, and Signal Processing -30 %N 3 %P 423-435 %O June %K * %A Achugbue, J.O. %D 1981 %T On the line breaking problem in text formatting %J SIGOA Newsletter (Proc ACM Symposium on Text manipulation, Portland, Oregon) %V 2 %N 1/2 %P 117-121 %O Spring/Summer %A Adams, D.N. %D 1979 %T The hitchhiker's guide to the galaxy %I Pan %C London, England %A Adams, J.B. %D 1976 %T A probability model of medical reasoning and the MYCIN model %J Mathematical Biosciences %V 32 %P 177-186 %A Aho, A.V. %A Corasick, M.J. %D 1975 %T Efficient string matching: an aid to bibliographic search %J Comm ACM %V 18 %N 6 %P 333-340 %O June %A Aikins, J.S. %D 1983 %T Prototypical knowledge for expert systems %J Artificial Intelligence %V 20 %N 2 %P 163-210 %O February %K * %A Akers, G. %A Lennig, M. %D 1984 %T Intonation in text-to-speech synthesis: evaluation of algorithms %R Report %I Bell-Northern Research %C Verdun, QUE %K * %A Allebach, J.P. %A Liu, B. %D 1976 %T Analysis of halftone dot profile and aliasing in the discrete binary representation of images %J Optical Society of America %V 67 %N 9 %K * %A Allen, B.P. %A Wright, J.M. %D 1983 %T Integrating logic programs and schemata %J Proc 8th International Joint Conference on Artificial Intelligence %P 340-342 %A Allen, E.M. %D 1983 %T YAPS: yet another production system %R Report TR-1146 %I Maryland Artificial Intelligence Group, Computer Science Department, University of Maryland %C Maryland, MD %O December %K * %A Allen, J.F. %A Perrault, C.R. %D 1980 %T Analyzing intention in utterances %J Artificial Intelligence %V 15 %P 143-178 %K * %A Allen, J.F. %D 1984 %T Towards a general theory of action and time %J Artificial Intelligence %V 23 %P 123-154 %K * %A Almes, G.T. %A Black, A.P. %A Lazowska, E.L. %A Noe, J.D. %D 1985 %T The Eden system: a technical review %J IEEE Trans Software Engineering %V SE-11 %N 1 %P 43-59 %O January %K * %A Alvey, P. %D 1983 %T The problems of designing a medical expert system %J Proc Expert Systems 83 %I Churchill College %C Cambridge, England %P 30-42 %O December %K * %A Anderberg, M.R. %D 1973 %T Cluster analysis for applications %I Academic Press %C New York, NY %A Anderson, D.P. %A Hedin, R.C. %D 1982 %T Voice input/output module %J Voice Data Entry Systems Application Conference '82 %C San Mateo, CA %O September 21 %K * %A Anderson, J.R. %T Knowledge compilation: the general learning mechanism %K * %A Anderson, J.R. %A Reiser, B.J. %D 1985 %T The LISP tutor %J Byte %V 10 %N 4 %P 159-175 %O April %A Andreae, J.H. %D 1984 %T Numbers in the head %R Man-Machine Studies Progress Report UC-DSE/24 %P 5-28 %I Department of Electrical Engineering, University of Canterbury %C New Zealand %A Andreae, P.M. %D 1984 %T Constraint limited generalization: acquiring procedures from examples %J Proc American Association on Artificial Intelligence %C Austin, TX %O August %K * %A Andreae, P.M. %D 1984 %T Justified generalization: acquiring procedures from examples %R PhD Thesis %I Department of Electrical Engineering and Computer Science, MIT %A Andreae, P.M. %D 1986 %T Justified generalization %J Proc International Conference on Future Advances in Computing %C Christchurch, New Zealand %O February 17-21 %K * %A Andreka, H. %A Nemeti, I. %A Sain, I. %D 1982 %T A complete logic for reasoning about programs via nonstandard model theory I %J Theoretical Computer Science %V 17 %P 193-212 %K * %A Andreka, H. %A Nemeti, I. %A Sain, I. %D 1982 %T A complete logic for reasoning about programs via nonstandard model theory II %J Theoretical Computer Science %V 17 %P 259-278 %K * %A Andrew, A.M. %D 1981 %T Autopoiesis \(em allopoiesis interplay %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 157-166 %A Anon %D 1972 %T Holography and computer generated holograms %I Mills and Boom %C London, England %K * %A ANSI %D 1983 %T Videotex/Teletext presentation level protocol syntax (Draft) %I American National Standards Committee X3 -- Information Processing Systems, Technical Committee X3L2 -- Codes and character sets %C New York, NY %O June %A Anson, E. %D 1982 %T The device model of interaction %J Computer Graphics %V 16 %N 2 %P 107-114 %O July %K * %A Aoki, M. %D 1965 %T Optimal control of partially observable markovian systems %J J Franklin Institute %V 280 %N 5 %O November %K * %A Ascher, R.N. %A Nagy, G. %D 1974 %T A means for achieving a high degree of compaction on scan-digitized printed text %J IEEE Trans Computers %V C-23 %N 11 %P 1174-1179 %O November %K * %A Ash, W.L. %D 1981 %T MXEC: parallel processing with an advanced macro facility %J Comm ACM %V 24 %N 8 %P 502-509 %K * %A Ashby, W.R. %D 1960 %T Design for a brain: the origin of adaptive behavior %I Wiley %C New York, NY %O (second edition) %A Askwall, S. %D 1985 %T Computer supported reading vs reading text on paper: a comparison of two reading situations %J IJMMS %V 22 %N 4 %P 425-439 %O April %A Atkinson, H.H. %A Gargantini, I. %A Ramanath, M.V.S. %D 1984 %T Determination of the 3D border by repeated elimination of internal surfaces %J Computing %V 32 %P 279-295 %K * %A Attardi, G. %A Simi, M. %D 1982 %T Semantics of inheritance and attributions in the description system Omega %R AI Memo 642 %I MIT Artificial Intelligence Laboratory %O January %K * %A Axelrod, R. %D 1984 %T The evolution of cooperation %I Basic Books %C New York, NY %A Backer, D. %A Gano, S. %D 1982 %T Dynamically alterable videodisk displays %J Proc Graphics Interface 82 %C Toronto, ON %P 365- %O May 17-21 %A Baecker, R. %A Marcus, A. %D 1983 %T On enhancing the interface to the source code of computer programs %J Proc ACM CHI 83 Human Factors in Computing Systems %P 251-255 %C Boston, MA %O December 12-15 %K * %A Bailey, D. %D 1985 %T University of Salford Lisp/Prolog system %J Software -- Practice and Experience %V 15 %N 6 %P 595-609 %O June %K * %A Ball, G.H. %A Hall, D.J. %D 1965 %T ISODATA, a novel method of data analysis and pattern classification %R Report AD 699616 %I Stanford Research Institute %C Stanford, CA %A Bandyopadhyay, S. %A Hughes, J.G. %A Smith, F.J. %A Sen, K. %T A generalized scientific information system %R Report %I Computer Science Department, Queen's University of Belfast %C Belfast, Northern Ireland %K * %A Barber, G.R. %D 1981 %T Record of the workshop on research in office semantics %R AI Memo 620 %I MIT %O February %K * %A Barber, G.R. %D 1982 %T Office semantics %R PhD Thesis %I MIT %O February %A Barber, G.R. %D 1983 %T Supporting organizational problem solving with a workstation %J ACM Trans Office Information Systems %V 1 %N 1 %P 45-67 %O January %K * %A Barber, G.R. %A de\|Jong, P.S. %A Hewitt, C. %D 1983 %T Semantic support for work in organizations %R AI Memo 719 %I MIT Artificial Intelligence Laboratory %O April %K * %A Barnett, J.A. %D 1981 %T Computational methods for a mathematical theory of evidence %J Proc 7th International Joint Conference on Artificial Intelligence %P 868-875 %C Vancouver, BC %O August %A Barrow, H.G. %D 1979 %T Artificial intelligence: state of the art %R Technical Note 198 %I SRI International %C Menlo Park, CA %O October %A Barsky, B.A. %A Beatty, J.C. %D 1982 %T Varying the betas in beta-splines %R Report CS-82-49 %I University of Waterloo %O December %K * %A Barwise, J. %A Perry, J. %D 1983 %T Situations and attitudes %I MIT Press %C Cambridge, MA %A Barwise, J. %D 1985 %T The situation in logic II: conditionals and conditional information %R Report CSLI-85-21 %I Center for the study of language and information, Stanford University %C Stanford, CA %O January %K * %A Bates, E. %D 1979 %T The emergence of symbols %I Academic Press %A Beer, S. %D 1980 %R Preface to \fIAutopoiesis and cognition\fR (Maturana and Varela, 1980) %A Bell, T.C. %D 1985 %T Better OPM/L text compression %R Internal Report %I Computer Science Department, University of Canterbury %C Christchurch, New Zealand %K * %A Bell, T.C. %A Moffat, A.M. %D 1986 %T A note on the DMC data compression scheme %R Internal Report %I Computer Science Department, University of Canterbury %C Christchurch, New Zealand %A Bell, T.C. %D 1986 %T An introduction to text compression %R Internal Report %I Computer Science Department, University of Canterbury %C Christchurch, New Zealand %A Bellanger, M.G. %A Daguet, J.L. %A Lepagnol, G.P. %D 1974 %T Interpolation, extrapolation, and reduction of computation speed in digital filters %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-22 %N 4 %P 231-235 %O August %K * %A Benest, I.D. %A Jones, G. %D 1982 %T Computer emulation of books %J Proc IEE Conference Man-Machine Systems %P 267-271 %C Manchester, England %O July %A Benest, I.D. %A Potok, M.H.N. %D 1984 %T Wayfinding: an approach using signposting techniques %J Behaviour and Information Technology %V 3 %N 2 %P 99-107 %K * %A Bentley, J.L. %A Friedman, J.H. %D 1979 %T Data structures for range searching %J Computing Surveys %V 11 %N 4 %P 397-409 %O December %K * %A Bentley, J.L. %D 1980 %T Multidimensional divide-and-conquer %J Comm ACM %V 23 %N 4 %P 214-229 %K * %A Bentley, J.L. %A Sleator, D.D. %A Tarjan, R.E. %A Wei, V.K. %D 1986 %T A locally adaptive data compression scheme %J Comm ACM %V 29 %N 4 %P 320-330 %O April %A Berglund, E.J. %A Cheriton, D.R. %T Amaze -- A distributed multi-player game program using the Distributed V Kernel %K * %A Bewley, W.L. %A Roberts, T.L. %A Schroit, D. %A Verplank, W.L. %D 1983 %T Human factors testing in the design of Xerox's 8010 `Star' office workstation %J Proc ACM CHI 83 Human Factors in Computing Systems %P 72-77 %C Boston, MA %O December 12-15 %K * %A Bezdek, J.C. %D 1980 %T A convergence theorem for the fuzzy ISODATA clustering algorithms %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-2 %N 1 %P 1-8 %O January %K * %A Bibel, W. %D 1983 %T Matings in matrices %J Comm ACM %V 26 %N 11 %P 844-852 %O November %K * %A Biederman, I. %D 1985 %T Human image understanding: recent research and a theory %J Computer Vision, Graphics, and Image Processing %V 32 %N 1 %P 29-73 %O October. %A Bigelow, C. %A Day, D. %D 1983 %T Digital typography %J Scientific American %V 249 %N 2 %P 106-119 %O August %K * %A Bigelow, C. %D 1984 %T Principles of font design for the personal workstation %R Research Report %I Stanford University %K * %A Bigelow, C. %D 1986 %T Notes on typeface protection %K * %A Birkhoff, G. %D 1967 %T Lattice theory %I American Mathematical Society %C Providence, RI %A Birrell, A.D. %A Levin, R. %A Needham R.M. %A Schroeder, M.D. %D 1982 %T Grapevine: an exercise in distributed computing %J Comm ACM %V 25 %N 4 %P 260-274 %O April %K * %A Birtwistle, G.M. %A Dahl, O.J. %A Myhrhaug, B. %A Nygaard, K. %D 1973 %T Simula Begin %I Auerbach %C Philadelphia, PA %A Birtwistle, G. %A Cleary, J.G. %A Joyce, J. %A Liblong, B. %A Unger, B.W. %A Witten, I.H. %A Wyvill, B.L.M. %D 1984 %T A simulation environment %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 290-296 %O May %K KConference %A Blakeslee, T.R. %D 1980 %T The right brain %I MacMillan %C London, England %A Bobrow, D.G. %D 1985 %T If Prolog is the answer, what is the question? or What it takes to support AI programming paradigms %J IEEE Trans Software Engineering %V SE-11 %N 11 %P 1401-1408 %O November %K * %A Boehm-Davis, D.A. %A Fregly, A.M. %D 1983 %T Documentation of concurrent programs %J Proc ACM CHI 83 Human Factors in Computing Systems %P 256-261 %C Boston, MA %O December 12-15 %K * %A Bonham, M. %A Witten, I.H. %D 1984 %T Towards distributed document preparation with interactive and noninteractive viewing %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 365-372 %O May %K KConference %A Bonham, M. %A Witten, I.H. %D 1985 %T Towards distributed document preparation with interactive and noninteractive viewing %J Infor %V 23 %N 4 %P 365-388 %O November %K KJournal %A Bonham, M. %A Witten, I.H. %D 1985 %T More on `A large font virtual terminal interface: a software prosthesis for the visually impaired' %J Comm ACM %V 28 %N 11 %P 1236-1237 %O November %K KCorrespondence %A Bonham, M. %A Witten, I.H. %D 1985 %T Shape \(em a unifying concept in document layout %J Proc PROTEXT II -- Second International Conference on Text Processing Systems %I Boole Press %C Dublin, Ireland %P 126-132 %O October %K KConference %A Bonner, S. %A Shin, K. %D 1982 %T A comparative study of robot languages %J IEEE Computer %V 15 %N 12 %P 82-96 %A Boose, J.H. %D 1986 %T Rapid acquisition and combination of knowledge from multiple experts in the same domain %J Proc International Conference on Future Advances in Computing %C Christchurch, New Zealand %O February 17-21 %K * %A Booth, T.L. %D 1984 %T Computer education %J IEEE Computer %V 17 %N 10 %P 57-68 %O October %K * %A Borning, A. %D 1981 %T The programming language aspects of ThingLab, a constraint-oriented simulation laboratory %J ACM Trans Programming Languages and Systems %V 3 %N 4 %P 353-387 %O October %K * %A Bouachache, B. %D 1983 %T Wigner analysis of time-varying signals %B Signal processing II: Theories and applications %E H.W.Schussler %I Elsevier Science Publishers B.V. (North Holland) %P 703-706 %K * %A Bouachache, B. %A Rodriguez, F. %D 1984 %T Recognition of time-varying signals in the time-frequency domain by means of the Wigner distribution %J Proc International Circuits and Systems Symposium %K * %A Bouachache, B. %A Whitehouse, H.J. %D 1985 %T Seismic applications of the Wigner-Ville distribution %J Proc International Circuits and Systems Symposium %C San Jose, CA %O May 5 %K * %A Boulton, P.I.P. %A Lee, E.S. %D 1983 %T The performance of Hubnet %J Proc International Electrical, Electronics Conference %V 2 %P 450-453 %C Toronto, ON %O September 26-28 %K * %A Bower, G.H. %A Black, J.B. %D 1979 %T Scripts in memory for text %J Cognitive Psychology %V 11 %P 177-220 %K * %A Brachman, R.J. %D 1983 %T What IS-A is and isn't: an analysis of taxonomic links in semantic networks %J IEEE Computer %V 16 %N 10 %P 30-36 %O October %K * %A Brachman, R.J. %A Schmolze, J.G. %D 1985 %T An overview of the KL-ONE knowledge representation scheme %J Cognitive Science %V 9 %N ii %P 171-216 %K * %A Bramer, M.A.\0(Editor) %D 1985 %T Research and development in expert systems %I Cambridge University Press %C Cambridge, England %O (Proc 4th Conference of BCS Group on Expert Systems, December 1984) %A Bramwell, B. %D 1984 %T Browsing around a manual %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 438-442 %O May %K * %A Britton, B.K. %A Black, J.B.\0(Editors) %D 1985 %T Understanding expository text: a theoretical and practical handbook for analyzing explanatory text %I Erlbaum %C Hillsdale, NJ %A Brown, K.Q. %D 1979 %T Voroni diagrams from convex hulls %J Information Processing Letters %V 9 %N 5 %P 223-228 %O December %K * %A Brown, P.J. %D 1984 %T Interactive documentation %R Internal Report %I Computing Laboratory, University of Kent %O April %K * %A Brown, J.S. %A Burton, R.R. %D 1975 %T Multiple representations of knowledge for tutorial reasoning %B Representation of Learning %E D.G. Bobrow and A. Collins %I Academic Press %C New York, NY %A Brownston, L. %A Farrell, R. %A Kant, E. %A Martin, N. %D 1985 %T Programming expert systems in OPS-5: an introduction to rule-based programming %I Addison-Wesley %C Reading, MA %A Brunner, J. %D 1975 %T The shockwave rider %I Ballantine %C New York, NY %A Bruynooghs, M. %D 1982 %T Adding redundancy to obtain more reliable and more readable Prolog programs %J Proc 1st International Logic Programming Conference %C Marseille, France %P 129-133 %O September 14-17 %K * %A Bryant, J. %D 1979 %T On the clustering of multidimensional pictorial data %J Pattern Recognition %V 11 %P 115-125 %K * %A Buchman, C. %A Berry, D.M. %T An adaptation of the Unix DITROFF for formatting bidirectional text %I Computer Science Department, University of California %C Los Angeles, CA %K * %A Bundy, A. %A Silver, B. %A Plummer, D. %D 1985 %T An analytical comparison of some rule-learning programs %J Artificial Intelligence %V 27 %P 137-181 %A Bundy, A. %D 1983 %T The computer modelling of mathematical reasoning %A Academic Press %C London, England %A Burton, R.R. %A Brown, J.S. %D 1979 %T An investigation of computer coaching for informal learning activities %J IJMMS %V 11 %N 1 %P 5-24 %O January %A Bush, V. %D 1945 %T As we may think %J Atlantic Monthly %P 101 %O July %A Byrd, R.J. %A Smith, S.E. %A de\|Jong, S.P. %D 1982 %T An actor-based programming system %J Proc SIGOA Conference on Office Information Systems %P 67-78 %C Philadelphia, PA %O June 21-23 %K * %A Campbell, F.W. %A Robson, J.G. %D 1967 %T Application of fourier analysis to the visibility of gratings %J Physiol %N 197 %P 551-566 %I University of Cambridge %K * %A Cannon, W.B. %D 1932 %T The wisdom of the body %I London %A Carroll, J.B. %D 1967 %T On sampling from a lognormal model of word-frequency distribution %B Computational analysis of present-day American English %E Kucera, H. and Francis, W.N. %I Brown University Press %C Providence, RI %P 406-424 %K * %A Carroll, J.B. %D 1966 %T Word-frequency studies and the lognormal distribution %E E.M.Zale %B Proc Conference on Language and Language Behavior %I Appleton-Century-Crofts %C New York, NY %P 213-235 %K * %A Carroll, J.M. %A Thomas, J.C. %D 1982 %T Metaphor and the cognitive representation of computing systems %J IEEE Trans Systems, Man and Cybernetics %V SMC-12 %N 2 %P 107-116 %O March/April %K * %A Carter, K.A. %D 1984 %T The Rainbow workstation in brief/A window manager for the Rainbow workstation %R Rainbow Group Note %I Computer Laboratory, University of Cambridge %O May %K * %A Casey, R.G. %A Friedman, T.D. %A Wong, K.Y. %D 1982 %T Automatic scaling of digital fonts %J IBM J Research and Development %V 26 %P 657-666 %A Casey, R.G. %A Nagy, G. %D 1984 %T Decision tree design using a probabilistic model TJ IEEE Trans Information Theory %V IT-30 %N 1 %P 93-99 %O January %K * %A Cater, J.P. %D 1983 %T Electronically speaking: computer speech generation %I Howard W. Sams %C Indianapolis, IN %A Catmull, E. %D 1981 %T New frontiers in computer animation %J American Cinematographer %P 157-163 %O October %A Cendrowska, J. %A Bramer, M.A. %D 1984 %T A rational reconstruction of the Mycin consultation system %J IJMMS %V 20 %P 229-317 %A Chapanis, A. %D 1984 %T Taming and civilizing computers %B Computer culture: the scientific, intellectual, and social impact of the computer %E Heinz R. Pagels %I New York Academy of Sciences %C New York, NY %P 202-219 %K * %A Chazelle, B. %D 1983 %T A decision procedure for optimal polyhedron partitioning %J Information Processing Letters %V 16 %P 75-78 %O February 26 %K * %A Cheeseman, P. %D 1985 %T In defense of probability %J Proc 10th International Joint Conference on Artificial Intelligence %P 100R2-1009 %A Cheriton, D.R. %A Zwaenepoel, W. %D 1983 %T The distributed V kernel and its performance for diskless workstations %R Report No STAN-CS-83-973 %I Stanford University, Computer Science Department %C Stanford, CA %O July %K * %A Christodoulakis, S %A Faloutsos, C. %D 1984 %T Design considerations for a message file server %J IEEE Trans Software Engineering %V SE-10 %N 2 %P 201-210 %O March %K * %A Ciminiere, L. %A Valenzano, A. %D 1984 %T iAPX 432 hardware fault handling mechanisms %K * %A Clancey, W.J. %D 1979 %T Tutoring rules for guiding a case method dialogue %J IJMMS %V 11 %P 25-49 %K * %A Clancey, W.J. %D 1983 %T The epistemology of a rule-based expert system \(em a framework for explanation %J Artificial Intelligence %V 20 %P 215-251 %K * %A Clancey, W.J. %A Shortliffe, E.H. \0(Editors) %D 1984 %T Readings in medical artificial intelligence %I Addison-Wesley %C Reading, MA %A Clarkson, T. %T Eye position sensor %R Section 7 of a report %I King's College %C London, England %K * %A Cleary, J.G. %D 1979 %T Analysis of an algorithm for finding nearest neighbors in Euclidean space %J ACM Trans Mathematical Software %V 5 %N 2 %P 183-192 %O June %K * %A Cleary, J.G. %D 1984 %T Compact hash tables using bidirectional linear probing %J IEEE Trans Computers %V C-33 %N 9 %P 828-834 %O September %A Cleary, J.G. %A Darragh, J.J. %D 1984 %T A fast compact representation of trees using hash tables %R Research Report 83/162/20 %I Computer Science Department, University of Calgary %O Submitted to \fIIEEE Trans Computers\fP %A Cleary, J.G. %A Witten, I.H. %D in preparation %T Universal data compression %A Clocksin, W.A. %D 1984 %T Introduction to Prolog %B Artificial Intelligence: tools, techniques, and applications %E T.O'Shea and M.Eisenstadt %I Harper and Row %C New York, NY %A Codd, E.F. %D 1968 %T Cellular automata %I Academic Press %C London, England %A Codd, E.F. %D 1978 %T How about recently? %B Databases: Improving usability and responsiveness %E B. Shneiderman (Ed.) %I Academic Press %C New York, NY %P 3-28 %A Cohen, E.S. %A Smith, E.T. %A Iverson, L.A. %D 1985 %T Constraint-based tiled windows %R Research Report %I Computer Science Department, Carnegie-Mellon University %A Cohen, J. %D 1985 %T Describing Prolog by its interpretation and compilation %J Comm ACM %V 28 %N 12 %P 1311-1324 %O December %K * %A Colby, K.M. %D 1973 %T Simulations of belief systems %E R.C.Schank and K.M.Colby %B Computer models of thought and language %I Freeman %C San Francisco, CA %P 251-286 %A Colmaurer, A. %A Colmaurer, C. %D 1983 %T Prolog en 10 figures %J TSI %V 2 %N 4 %O July-August %K * %A Colmerauer, A. %D 1985 %T Prolog in 10 figures %J Comm ACM %V 28 %N 12 %P 1296-1310 %O December %K * %A Colmaurer, A. %T An interesting subset of natural language %K * %A Comer, D.E. %A Peterson, L.L. %T Conversation-based mail %J ACM Transactions on computer systems %V 4 %N 4 %O November %K * %A Computer\|Science\|Department %D 1983 %T CPSC Student Handbook %I University of Calgary %A Coombs, M. %A Alty, J. %D 1984 %T Expert systems: an alternative paradigm %J IJMMS %V 20 %N 1 %P 21-43 %O January %K * %A Corbett, C. %D 1983 %T MC nroff/troff macros reference manual %R Report EES-MMS-1983-2 %I Department of Electrical Engineering Science, University of Essex %C Colchester, Essex, UK %K * %A Corbett, C. %D 1983 %T Figure processing within nroff %J Presented at EUUG Meeting %C Dublin, Ireland %O September %K * %A Cormack, G.V. %A Horspool, R.N. %D 1984 %T Algorithms for adaptive Huffman codes %J Information Processing Letters %V 18 %N 3 %P 159-166 %O March %K * %A Cormack, G.V. %A Horspool, R.N. %D 1985 %T Data compression using dynamic Markov modelling %R Research Report %I Computer Science Department, University of Waterloo %O April; submitted to Comm ACM %K * %A Costigan, D.M. %D 1978 %T Electronic delivery of documents and graphics %I Van Nostrand Reinhold %C New York, NY %A Coulon, D. %A Kayser, D. %D 1979 %T Construction of natural language sentence acceptors by a supervised-learning technique %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-1 %N 1 %P 94-99 %O January %K * %A Cove, J.F. %A Walsh, B.C. %D 1988 %T A taxonomy of browsing %R Working Paper 85/2 %I Computer Science Department, University of Liverpool %O April %K * %A Cox, B.J. %D 1986 %T Object oriented programming %I Addison-Wesley %C Reading, MA %A Crochiere, R.E. %A Rabiner, L.R. %D 1975 %T Optimum FIR digital filter implementations for decimation, interpolation, and narrow-band filtering %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-23 %N 5 %P 444-456 %O October %K * %A Crochiere, R.E. %A Rabiner, L.R. %D 1976 %T Further considerations in the design of decimators and interpolators %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-24 %N 4 %P 296-311 %O August %K * %A Croft, W.B. %A Lefkowitz, L.S. %D 1984 %T Task support in an office system %J ACM Trans Office Information Systems %V 2 %N 3 %P 197-212 %O July %K * %A Croft, W.B. %D 1984 %T The role of context and adaptation in user interfaces %J IJMMS %V 21 %N 4 %P 283-292 %O October %A Csuri, C. %D 1974 %T Computer graphics and art %J Proc IEEE %O April %A Cuff, R.N. %D 1982 %T Database query using menus and natural language fragments %R PhD Thesis %I Man-Machine Systems Laboratory, Department of Electrical Engineering Science, University of Essex %C Colchester, Essex, UK %A Cuff, R.N. %D 1984 %T HERCULES: database query using natural language fragments %J Proc 3rd British National Conference on Database Systems %C Leeds %O July %K * %A Cullingford, R.E. %D 1978 %T Script application: computer understanding of newspaper stories %R PhD Thesis, Research Report 116 %I Yale University %A Cullingford, R.E. %A Krueger, M.W. %A Selfridge, M. %A Bienkowski, M.A. %D 1982 %T Automated explanations as a component of a computer-aided design system %J IEEE Trans Systems, Man and Cybernetics %V SMC-12 %N 2 %P 168-181 %O March/April %K * %A Cullingford, R.E. %A Pazzani, M.J. %D 1984 %T Word-meaning selection in multiprocess language understanding programs %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-6 %N 4 %P 493-509 %O July %K * %A Curry, G. %A Baer, L. %A Lipkie, D. %A Lee, B. %D 1982 %T Traits: an approach to multiple-inheritance subclassing %J ACM Conference on Office Information Systems %P 1-9 %O June %K * %A Damper, R.I. %A MacDonald, S.L. %D 1984 %T Template adaptation in speech recognition %J Proc Institute of Acoustics %V 6 %N 4 %P 293-299 %K * %A Damper, R.I. %A MacDonald, S.L. %D 1984 %T Statistical clustering procedures applied to low-cost speech recognition %J J Biomed Engineering %V 6 %P 265-271 %O October %K * %A Darragh, J.J. %A Witten, I.H. %A Cleary, J.G. %D 1983 %T Adaptive text compression to enhance a modem %R Research Report 83/132/21 %I Computer Science Department, University of Calgary %K KReport %A Davis, R. %D 1979 %T Interactive transfer of expertise: acquisition of new inference rules %J Artificial Intelligence %V 12 %N 2 %P 121-157 %K * %A Davis, R. %A Lenat, D.B. %D 1982 %T Knowledge-based systems in artificial intelligence %I McGraw Hill %C New York, NY %A Day, J.D. %A Zimmermann, H. %D 1983 %T The OSI reference model %J Proc IEEE %V 71 %N 12 %P 1334-1340 %O December %A Defude, B. %D 1984 %T Knowledge based systems versus thesaurus: an architecture problem about expert systems design %J Proc 3rd Joint BCS and ACM Symposium (King's College, Cambridge) %I Cambridge University Press %P 267-280 %O July %K * %A de\|Beaugrande, R. %D 1980 %T Text, discourse and process: towards a multidisciplinary science of texts %I Ablex Publishing Corporation %C Norwood, NJ %A de\|Jong, G. %D 1979 %T Prediction and substantiation: two processes that comprise understanding %J Proc International Joint Conference on Artificial Intelligence %C Tokyo, Japan %P 217-222 %O August %A de\|Jong, G. %D 1981 %T Generalizations based on explanations %J Proc IJCAI 81 %P 67-69 %K * %A de\|Jong, S.P. %D 1980 %T The system for business automation (SBA): a unified application development system %B Information Processing 80 %E S.H.Lavington %P 469-474 %I North Holland %K * %A de\|Leon, L. %A Harris, W.G. %A Evens, M. %D 1983 %T Is there really trouble with Unix? %J Proc ACM CHI 83 Human Factors in Computing Systems %P 125-129 %C Boston, MA %O December 12-15 %A Dietterich, T.G. %A Michalski, R.S. %D 1983 %T A comparative review of selected methods for learning from examples %B Machine learning %E R.S. Michalski, J.G. Carbonell, and T.M. Mitchell %I Tioga %P 41-81 %A Denning, P.J. %D 1982 %T Computer-based predictive writing %J Comm ACM %V 25 %N 5 %P 315-316 %O May %K * %A Denning, P.J. %D 1984 %T Educational ruminations %J Comm ACM %V 27 %N 10 %P 979-983 %O October %A Denning, P.J. %D 1985 %T The science of computing: what is Computer Science? %J American Scientist %V 73 %O January/February %K * %A Dewdney, A.K. %D 1984 %T Computer recreations %J Scientific American %V 250 %N 5 %P 14-22 %O May %A Downs, T. %A Cook, A.S. %A Rogers, G. %D 1984 %T A partitioning approach to yield estimation for large circuits and systems %J IEEE Trans Circuits and Systems %V CAS-31 %N 5 %P 472-485 %O May %K * %A Downs, T. %D 1985 %T An approach to the modeling of software testing with some applications %J IEEE Trans Software Engineering %V SE-11 %N 4 %P 375-386 %O April %K * %A Drummond, M. %D 1983 %T A proposal to study the cost-effectiveness of planning, acting, and sensing %R DAI Working paper %I Department of Artificial Intelligence, University of Edinburgh %K * %A Dubes, R. %A Jain, A.K. %D 1979 %T Validity studies in clustering methodologies %J Pattern Recognition %V 11 %P 225-254 %K * %A Duda, R. %A Gaschnig, J. %A Hart, P. %D 1979 %T Model design in the Prospector consultant system for mineral exploration %E D. Michie %B Expert systems in the microelectronic age %I Edinburgh University Press %K * %A Dumais, S. %A Landauer, T. %D 1982 %T Psychological investigations of natural terminology for command and query languages %B Directions in human/computer interactions %E Badre and Shneiderman %I Ablex Publishing Corporation %C Norwood, NJ %P 95-110 %A Dumais, S. %A Landauer, T. %D 1983 %T Using examples to describe categories %J Proc ACM CHI 83 Human Factors in Computing Systems %P 112-115 %C Boston, MA %O December 12-15 %K * %A Dunham, M.O. %A Gray, R.M. %D 1985 %T An algorithm for the design of labeled-transition finite-state vector quantization %J IEEE Trans Communications %V COM-33 %N 1 %P 83-89 %O January %K * %A Dyer, M.G. %D 1983 %T In-depth understanding %I MIT Press %C Cambridge, MA %A Dynkin, E.B. %D 1965 %T Controlled random sequences %J Theoretical Probability and its Applications %V X %N 1 %K * %A Eason, K.D. %A Damodaran, L %D 1979 %T Design procedures for user involvement and user support %J Infotech - Man Computer Communications %C London, England %A Edelsbrunner, H. %A Maurer, H.A. %D 1985 %T Finding extreme points in three dimensions and solving the post-office problem in the plane %J Information Processing Letters %V 21 %P 39-47 %O 10 July %K * %A Efron, B. %A Thisted, R. %D 1976 %T Estimating the number of unseen species: how many words did Shakespeare know? %J Biometrika %V 63 %N 3 %P 435-447 %K * %A Eisenstadt, E. %A Hasemar, T. %D 1985 %T An improved user interface for Prolog %E B.Shackel %B Human-Computer Interaction: Proceedings INTERACT 84 %I North Holland %P 109-113 %K * %A Ekeberg, O. %D 1986 %T Robust dictionary lookup using associative methods %R Research Report %I Computer Vision and Associative Pattern Processing Laboratory, Department of Computing Science, Royal Institute of Technology %C Stockholm, Sweden %K * %A Elias, P. %D 1955 %T Predictive coding: Part I and Part II %J IRE Trans Information Theory %V IT-1 %N 1 %P 16-33 %K * %A Elias, P. %D 1970 %T Bounds on performance of optimum quantizers %J IEEE Trans Information Theory %V IT-16 %N 2 %P 172-184 %O March %K * %A Elias, P. %D 1975 %T Universal codeword sets and representations of the integers %J IEEE Trans Information Theory %V IT-21 %N 2 %P 194-203 %O March %K * %A Elliott, S.J. %A Nelson, P.A. %D 1985 %T An algorithm for multichannel LMS adaptive filtering %R Research Report %I Institute of Sound and Vibration Research, University of Southampton %C Southampton, England %K * %A Ellis, C.A. %A Nutt, G.J. %D 1979 %T On the equivalence of office models %R Research Report SSL-79-8 %I Xerox PARC %O December %K * %A Ellis, C.A. %T Formal and informal models of office activity %R Research Report %I Xerox PARC %K * %A Ellis, C.A. %A Bernal, M. %D 1982 %T Officetalk-D: an experimental office information system %J Proc ACM Conference %P 131-140 %K * %A Embley, D.W. %A Nagy, G. %D 1981 %T Behavioral aspects of text editors %J Computing Surveys %V 13 %N 1 %P 33-70 %O March %K * %A Engel F.L. %A Andriessen J.J. %A Schmitz, H.J.R. %D 1983 %T What, where and whence: means for improving electronic data access %J IJMMS %V 18 %P 145-160 %A Englebart, D.C. %A English, W.K. %D 1968 %T A research center for augmenting human intellect %J Proc Fall Joint Computer Conference %V 33 %P 395-410 %I AFIPS Press %C Arlington, VA %A Ernvall, J. %A Nevalainen, O. %D 1984 %T Estimating the length of minimal spanning trees in compression of files %J BIT %V 24 %P 19-32 %K * %A Even, S. %A Rodeh, M. %D 1978 %T Economical encodings of commas between strings %J Comm ACM %V 21 %P 315-317 %O April %K * %A Even, S. %A Pratt, V. %A Rodeh, M. %D 1981 %T Linear algorithm for data compression via string matching %J J ACM %V 28 %N 1 %P 16-24 %O January %A Everitt, B. %D 1974 %T Cluster analysis %I Heineman %C London, England %A Fano, R.M. %D 1949 %T The transmission of information %R Technical Report 65 %I Research Laboratory of Electronics, MIT %C Cambridge, MA %A Feigenbaum, E.A. %A McCorduck, P. %D 1983 %T The fifth generation %I Addison-Wesley %C Reading, MA %A Ferrans, J.C. %D 1982 %T SEDL \(em a language for specifying integrity constraints on office forms %J SIGOA Newsletter (Proc SIGOA Conference on Office Information Systems) %V 3 %N 3/4 %P 123-130 %C Philadelphia, PA %O June 21-23 %K * %A Fikes, R. %A Kehler, T. %D 1985 %T The role of frame-based representation in reasoning %J Comm ACM %V 28 %N 9 %P 904-920 %O September %K * %A Filipski, A. %A Hanko, J. %D 1986 %T Making UNIX secure %J Byte %P 113-128 %O April %A Fillmore, C.J. %D 1968 %T The case for case %E E.Bach and R.T.Harms %B Universals in linguistic theory %P 1-88 %I Holt, Reinhart and Winston %C Chicago, IL %K * %A Finkel, R.A. %A Bentley, J.L. %D 1974 %T Quad trees -- a data structure for retrieval on composite keys %J Acta Informatica %V 4 %N 1 %P 1-9 %A Fischer, G. %A Lemke, A. %A Schwab, T. %D 1985 %T Knowledge-based help systems %J Proc Human Factors in Computer Systems %C San Francisco, CA %P 161-167 %O April %K * %A Fitter, M. %D 1979 %T Toward more natural interactive systems %J IJMMS %V 11 %P 339-350 %A Fogel, L.J. %A Owens, A.J. %A Walsh, M.J. %D 1966 %T Artificial intelligence through simulated evolution %I Wiley %A Foley, J.D. %A Wallace, V.L. %A Chan, P. %D 1984 %T The human factors of computer graphics interaction techniques %J IEEE Computer Graphics and Applications %V 4 %N 11 %P 13-48 %O November %K * %A Foster, J. %A Gray, R.M. %A Dunham, M.O. %D 1985 %T Finite-state vector quantization for waveform coding %J IEEE Trans Information Theory %V IT-31 %O May %A Freij, G.J. %A Cheetham, B.M.G. %D 1985 %T Improved sequential linear prediction by selective time-domain coefficient extraction %R Report %K * %A Friedman, J.H. %A Baskett, F. %A Shustek, L.J. %D 1975 %T An algorithm for finding nearest neighbors %J IEEE Trans Computers %V C-24 %P 1000-1006 %O October %K * %A Friedman, J.H. %A Bentley, J.L. %A Finkel, R.A. %D 1977 %T An algorithm for finding best matches in logarithmic expected time %J ACM Trans Mathematical Software %V 3 %N 3 %P 209-226 %O September %K * %A Fukunaga, K. %A Narendra, P.M. %D 1975 %T A branch and bound algorithm for computing \fIk\fP-nearest neighbors %J IEEE Trans Computers %V C-24 %P 750-753 %O July %K * %A Fulton, M.A. %T A social cognition research model for studying human-computer communication %R Research Report %I Oklahoma State University Business College %C Stillwater, OK %K * %A Gaines, B.R. %D 1976 %T On a danger in the assumption of causality %J IEEE Trans Systems, Man and Cybernetics %V SMC-6 %P 56-59 %A Gaines, B.R. %D 1981 %T Autopoiesis: some questions %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 145-154 %A Gaines, B.R. %D 1981 %T The technology of interaction -- dialog programming rules %J IJMMS %V 14 %N 1 %P 133-150 %O January %A Gaines, B.R. %D 1983 %T From word processing to image processing in office systems %J Proc International Electrical, Electronics Conference %V 2 %P 622-625 %C Toronto, ON %O September 26-28 %K * %A Gaines, B.R. %D 1984 %T Fundamentals of decision: probabilistic, possibilistic and other forms of uncertainty in decision analysis %J Studies in the Management Sciences %V 20 %P 47-65 %A Gaines, B.R. %D 1985 %T Expert systems and simulation in planning flexible manufacturing systems %J Proc Workshop on Coupling Symbolic and Numerical Computing in Expert Systems %I Boeing Computer Services AI Center %C Bellevue, WA %O August 27-29 %A Gaines, B.R. %D 1985 %T The design of expert systems for planning flexible manufacturing %R Research Report %I Computer Science Department, University of Calgary %A Gaines, B.R. %A Shaw, M.L.G %D 1986 %T Foundations of dialog engineering: the development of human-computer interaction Part II %J IJMMS %V 24 %N 2 %P 101-123 %O February %A Gaines, B.R. %D 1986 %T An overview of knowledge acquisition and transfer %J Proc AAAI Workshop on Knowledge Acquisition for Knowledge-based Systems %C Banff, AL %O November %A Galitz, W.O. %D 1980 %T Human factors in office automation %I Life Office Management Association %C Atlanta, GA %A Gardner, M. %T In which `monster' curves force redefinition of the word `curve' %J Scientific American %P 124-133 %K* %A Gargantini, I. %A Atkinson, H.H. %D 1984 %T Linear quadtrees: a blocking technique for contour filling %J Pattern Recognition %V 17 %N 3 %P 285-293 %K * %A Gargantini, I.A. %D 1983 %T Recent results on linear quadtrees and related techniques %R Report 111 %I Computer Science Department, University of Western Ontario %C London, ON %O December (to appear in \fIPattern recognition\fP) %K * %A Garudadri, H. %A Beddoes, M.P. %A Gilbert, J.H.V. %A Benguerel, A.P. %T Identification of invariant acoustic cues in stop consonants using the Wigner distribution %J %K * %A Garvey, T.D. %A Lowrance, J.D. %A Fischler, M.A. %D 1981 %T An inference technique for integrating knowledge from disparate sources %J Proc 7th International Joint Conference on Artificial Intelligence %P 319-325 %C Vancouver, BC %O August %K * %A Gehani, N.H. %D 1983 %T An electronic form system -- an experience in prototyping %J Software -- Practice and Experience %V 13 %P 479-486 %K * %A Gehani, N.H. %D 1983 %T High level form definition in office information systems %J Computer J %V 26 %N 1 %P 52-59 %K * %A Geller, V.J. %A Lesk, M.E. %D 1981 %T How users search: a comparison of menu and attribute retrieval systems on a library catalog %R Internal Report %I Bell Laboratories %K * %A Genesereth, M.R. %A Ginsberg, M.L. %D 1985 %T Logic programming %J Comm ACM %V 28 %N 9 %P 933-941 %O September %K * %A Genesereth, M.R. %A Ginsberg, M.L. %A Rosenschein, J.S. %D 1985 %T Solving the prisoner's dilemma %R Research Report STAN-CS-84-1032 %I Computer Science Department, Stanford University %C Stanford, CA %K * %A Giles, R. %D 1976 %T Lucasiewicz logic and fuzzy set theory %J IJMMS %V 8 %P 313-327 %A Ginsberg, M.L. %D 1985 %T Does probability have a place in non-monotonic reasoning? %J Proc IJCAI %P 107-110 %K * %A Georgeff, M. %A Lansky, A.L. %A Bessiere, P. %D 1985 %T A procedural logic %J Proc International Joint Conference on Artificial Intelligence %C Los Angeles, CA %O August %K * %A Gersham, A.V. %D 1982 %T A framework for conceptual analyzers %E W.G. Lenhert and M.H. Ringle %B Strategies for natural language processing %I Lawrence Erlbaum Associates %P 177-202 %A Gersho, A. %D 1979 %T Asymptotically optimal block quantization %J IEEE Trans Information Theory %V IT-25 %N 4 %P 373-380 %O July %K * %A Gevarter, W.B. %D 1983 %T An overview of computer-based natural language processing %R NASA Technical Memorandum 85635 %C Washington, DC %A Gevarter, W.B. %D 1983 %T Expert systems: limited but powerful %J IEEE Spectrum %P 39-45 %O August %A Gibbon, D. %A Richter, H.\0(Editors) %D 1984 %T Intonation, Accent and Rhythm %I de Gruyter %C Berlin %A Gibbs, R.W. %A Tenney, Y.J. %D 1980 %T The concept of scripts in understanding stories %J J Psycholinguistic Research %V 9 %N 3 %P 275-284 %K * %A Gibson, B. %A Wittig, R. %D 1983 %T The Develnet LAN: architecture and experience %J Proc International Electrical, Electronics Conference %V 1 %P 26-29 %C Toronto, ON %O September 26-28 %K * %A Girill, T.R. %A Luk, C.H. %D 1983 %T DOCUMENT: an interactive, online solution to four documentation problems %J Comm ACM %V 26 %N 5 %P 328-337 %O May %A Glinert, E.P. %A Tanimoto, S.L. %D 1984 %T Pict: an interactive graphical programming environment %J IEEE Computer %V 17 %N 11 %P 7-25 %O November %K * %A Glinert, E.P. %A Ladner, R.E. %D 1984 %T A large font virtual terminal interface %J Comm ACM %V 27 %N 7 %P 567-572 %O June %K * %A Godfrey, D. %A Chang, E.\0(Editors) %D 1981 %T The Telidon book %I Press Porcepic %C Toronto, ON %A Goldshlager, L.M. %D 1980 %T Short algorithms for space-filling curves %J Software -- Practice and Experience %V 11 %P 99-100 %O September %K * %A Good, D.I. %D 1982 %T The proof of a distributed system in Gypsy %R Technical Report 30 %I Institute for Computing Science, University of Texas at Austin %C Austin, TX %O September %K * %A Gordon, J. %A Shortliffe, E.H. %D 1984 %T The Dempster-Shafer theory of evidence %B Rule-based expert systems %E B.G.Buchanan and E.H.Shortliffe %I Addison-Wesley %C Reading, MA %P 272-292 %A Gordon, J. %A Shortliffe, E.H. %D 1985 %T A method for managing evidential reasoning in a hierarchical hypothesis space %J Artificial Intelligence %V 26 %P 323-357 %A Gosling, J.A. %D 1981 %T A redisplay algorithm %J SIGOA Newsletter (Proc ACM Symposium on Text manipulation) %C Portland, OR %V 2 %N 1/2 %P 123-129 %O Spring/Summer %A Gosling, J.A. %A Rosenthal, D.S.H. %D 1983 %T A network window-manager %R Report %I Information Technology Center, Carnegie-Mellon University %C Pittsburgh, PA %K * %A Grampp, F.T. %A Morris, R.H. %D 1984 %T UNIX operating system security %J Bell System Technical J %V 62 %N 8, part 2 %P 1649-1672 %O October %A Grasser, A.C. %D 1981 %T Prose comprehension beyond the word %I Springer-Verlag %C New York, NY %A Gray, R.M. %A Kieffer, J.C. %A Linde, Y. %D 1980 %T Locally optimal block quantizer design %J Information and Control %V 45 %P 178-198 %K * %A Gray, R.M. %D 1984 %T Hardware realization of waveform vector quantizers %J IEEE Trans %V SAC-2 %N 2 %A Green, M. %D 1982 %T Towards a user interface prototyping system %J Proc Graphics Interface 82 %P 37-45 %K * %A Greenberg, S. %D 1984 %T User modeling in interactive computer systems %R MSc Thesis %I Computer Science Department, University of Calgary %A Greenberg, S. %A Witten, I.H. %D 1984 %T Comparison of menu displays for ordered lists %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 464-469 %O May %K KConference %A Greenberg, S. %A Witten, I.H. %D 1985 %T Adaptive personalized interfaces -- a question of viability %J Behaviour and Information Technology %V 4 %N 1 %P 31-45 %O January-March %K KJournal %A Greenberg, S. %A Witten, I.H. %D 1985 %T Interactive end-user creation of workbench hierarchies within a window system %J Proc Canadian Information Processing Society Conference %C Montreal, QUE %P 408-416 %O May %K KConference %A Greenberg, S. %A Peterson, M. %A Witten, I.H. %D 1986 %T Issues and experiences in the design of a window management system %J Proc Canadian Information Processing Society Conference %C Edmonton, AL %P 33-44 %K KConference %A Grice, H.P. %D 1957 %T Meaning %J Philosophical Review %V LXVI %N 3 %P 377-388 %K * %A Grice, H.P. %D 1969 %T Utterer's meaning and intentions %J Philosophical Review %V LXXVIII %N 2 %P 147-177 %K * %A Grossner, C.P. %A Radhakrishnan, T. %A Pospiech, A. %D 1983 %T An integrated workstation for the visually handicapped %J IEEE Micro %P 8-16 %O June %K * %A Gullichsen, E. %A Chang, E. %D 1985 %T Generative design in architecture using an expert system %R Research Report %I Computer Science Department, University of Victoria %O February %K * %A Hagelbarger, D.W. %A Thompson, R.A. %D 1983 %T Experiments in teleterminal design %J IEEE Spectrum %P 40-45 %O October %K * %A Halbert, D.C. %D 1984 %T Programming by example %R Technical Report %I Xerox PARC, (Office Products Division) %C Palo Alto, CA %O December %A Hammond, P. %A Sergot, M. %D 1983 %T A Prolog shell for logic based expert systems %J Proc Expert Systems 83 %I Churchill College %C Cambridge, England %P 95-104 %O December %K * %A Hammond, P. %D 1984 %T Representation of DHSS regulations as a logic program %J Proc Expert Systems 83 %I Churchill College %C Cambridge, England %P 225-235 %O December %K * %A Hanson, S.J. %A Kraut, R.E. %A Farber, J.M. %D 1984 %T Interface design and multivariate analysis of UNIX command use %J ACM Trans Office Information Systems %V 2 %N 1 %O March %A Harrison, A.F. %A Bramson, R.M. %T The art of thinking %I Berkley Books %C New York, NY %K * %A Harth, E. %D 1982 %T Windows on the mind %I Harvester Press %C Brighton, Sussex %A Hartigan, J.A. %D 1975 %T Clustering algorithms %I Wiley %A Hartley, J. %D 1978 %T Designing instructional text %I Kogan Page %C London, England %A Hartley, J. %D 1982 %T Designing instructional text %E D.H. Jonassen %B The technology of text %I Educational Technology Publications %C Englewood Cliffs, NJ %A Hasling, D.W. %A Clancey, W.J. %A Rennels, G. %D 1984 %T Strategic explanations for a diagnostic consultation system %J IJMMS %V 20 %P 3-19 %K * %A Haugeland, J. %D 1979 %T Understanding natural language %V LXXVI %N 11 %P 619-632 %O November %K * %A Hayes, P. %T The naive physics manifesto %K * %A Hayes, P.J. %A Ball, E. %A Reddy, R. %D 1983 %T Breaking the man-machine communication barrier %J IEEE Computer %P 19-30 %O March %K * %A Hayes, P.J. %D 1984 %T Executable interface definitions using form-based interface abstractions %R Report CMS-CS-84-110 %I Computer Science Department, Carnegie-Mellon University %K * %A Hayes, P.J. %A Szekely, P.A. %A Lerner, R.A. %D 1985 %T Design alternatives for user interface management systems based on experience with Cousin %J Proc Human Factors in Computer Systems %C San Francisco, CA %P 169-175 %O April %K * %A Hayes, P.J. %A Lerner, R.A. %A Szekely, P.A. %T The COUSIN user interface project %K * %A Hayes-Roth, F. %D 1985 %T Rule-based systems %J Comm ACM %V 28 %N 9 %P 921-932 %O September %K * %A Hays, D.G. %D 1964 %T Dependency theory -- a formalism and some observations %J Language %V 40 %P 511-25 %A Heath, F.G. %A Foulk, P.W. %A Li, D.Y. %D 1984 %T Analysis and restructuring of concurrent systems using Prolog %J Proc IEEE Part E %V 131 %N 5 %P 169-176 %O September %K * %A Heckbert, P. %D 1982 %T Color image quantization for frame buffer display %J Proc SIGGRAPH 82 %C Boston, MA %P 297-307 %O July %K * %A Held, G. %D 1984 %T Data compression: techniques and applications %I Wiley %C New York, NY %A Hendrix, G.G %A Sacerdoti, E.D. %A Sagalowicz, D. %A Slocum, J. %D 1978 %T Developing a natural language interface to complex data %J ACM Trans Database Systems %V 3 %N 2 %O June %A Hester, J.H. %A Hirschberg, D.S. %D 1985 %T Self-organizing linear search %J Computing Surveys %V 17 %N 3 %P 295-311 %O September %A Hewitt, C. %D 1977 %T Viewing control structures as patterns of passing messages %J Artificial Intelligence %V 8 %P 323-364 %A Hewitt, C. %A de\|Jong, P.S. %D 1982 %T Open systems %R AI Memo 691 %I MIT Artificial Intelligence Laboratory %O December %K * %A Hewitt, C. %A de\|Jong, P.S. %D 1983 %T Analyzing the roles of descriptions and actions in open systems %R AI Memo 727 %I MIT Artificial Intelligence Laboratory %O April %K * %A Hewitt, C. %A de\|Jong, P.S. %D 1983 %T Message passing semantics as a foundation for reasoning in open systems %R Research Report %I MIT Artificial Intelligence Laboratory %O May %K * %A Hibbard, P. %D 1983 %T User manual for MINT -- the SPICE document preparation system %R Technical Report %I Computer Science Department, Carnegie-Mellon University %C Pittsburgh, PA %A Hilbert, D. %D 1891 %T Ueber die stetige Abbildung einer Linie auf ein Flachenstuck %J Math Annalen %V 38 %P 459-460 %K * %A Hill, D.R. %A Dohrn, C. %A Darragh, J. %A Esau, R. %A Levinson, D. %A Unger, B. %A Witten, I.H. %D 1984 %T Using speech output as a medium for human-computer dialogue %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 470-476 %O May %K KConference %A Hill, D.R. %A Witten, I.H. %A Neal, R. %A Lomow, G. %D 1984 %T Jecl and Hide: practical questions for the Jade user interface %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 373-380 %O May %K KConference %A Hiltz, S.R. %A Turoff, M. %D 1985 %T Structuring computer-mediated communication systems to avoid information overload %J CACM %V 28 %N 7 %P 680-689 %O July %A Hintikka, K.J. %D 1975 %T The intentions of intentionality %I D. Reidel %C Dordrecht, Holland %A Ho, C.S. %A Hong, Y.C. %A Kuo, T.S. %D 1986 %T A society model for office information systems %J ACM Trans Office Information Systems %V 4 %N 2 %P 104-137 %O April %K * %A Horspool, R.N. %A Cormack, G.V. %D 1984 %T A general-purpose data compression technique with practical computer applications %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 138-141 %O May %K * %A Horspool, R.N. %A Cormack, G.V. %D 1985 %T Comments on `Data compression using static Huffman code-decode tables' %O submitted to CACM, November 1985 %K * %A Horspool, R.N. %A Cormack, G.V. %D 1986 %T Dynamic Markov modelling -- a prediction technique %J Proc International Conference on the System Sciences %C Honolulu, HA %O January %K * %A Hosticka, B.J. %D 1985 %T Performance comparison of analog and digital circuits %J Proc IEEE %V 73 %N 1 %P 25-29 %O January %A Hou, H.S. %A Andrews, H.C. %D 1978 %T Cubic splines for image interpolation and digital filtering %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-26 %N 6 %P 508-517 %O December %K * %A Hovy, E.H. %T Integrating text planning and production in generation %J Proc IJCAI %P 848-851 %K * %A Hunter, R. %A Robinson, A.H. %D 1980 %T International digital facsimile coding standards %J Proc IEEE %V 68 %N 7 %P 854-867 %O July %K * %A Hutchings, E. %D 1983 %T The autonomous Viking %J Science %V 219 %P 803-808 %O February 18 %A Jackendoff, R. %D 1985 %T Semantics and cognition %I MIT Press %C Cambridge, MA %A Jakobsson, M. %D 1985 %T Compression of character strings by an adaptive dictionary %J BIT %V 25 %N 4 %P 593-603 %K * %A Jantsch, E. %D 1981 %T Autopoiesis: a central aspect of dissipative self-organization %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 65-88 %A Jarvis, J.F. %D 1984 %T Robotics %J IEEE Computer %P 283-292 %O October %K * %A Jarvis, R.A. %D 1983 %T Growing polyhedral obstacles for planning collision-free paths %J Australian Computer J %V 15 %N 3 %P 103-111 %O August %K * %A Jaynes, J. %D 1976 %T The origin of consciousness in the breakdown of the bicameral mind %I Houghton Mifflin %C Boston, MA %A Jefferson, D.R. %D 1985 %T Virtual time %J ACM Trans Programming Languages and Systems %V 7 %N 3 %P 404-425 %O July %K * %A Johnson, W.L. %A Soloway, E. %A Cutler, B. %A Draper, S.W. %D 1983 %T Bug catalogue: I %R Research Report %I Cognition and Programming Project, Computer Science Department, Yale University %O October %K * %A Jones, H. %D 1976 %T Stanley Morison displayed %I Frederick Muller %C London, England %A Jones, L.P. %A Iyengar, S.S. %D 1984 %T Space and time efficient virtual quadtrees %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-6 %N 2 %P 244-247 %O March %K * %A Kaczmarek, T. %A Mark, W. %A Sondheimer, N. %D 1983 %T The Consul/CUE interface: an integrated interactive environment %J Proc ACM CHI 83 Human Factors in Computing Systems %P 98-102 %C Boston, MA %O December 12-15 %K * %A Kaehler, T. %A Patterson, D. %D 1986 %T A taste of Smalltalk %I W.W. Norton %C New York, NY %A Kang, A.N.C. %A Lee, R.C.T. %A Chang, C-L. %A Chang, S-K. %D 1977 %T Storage reduction through minimal spanning trees and spanning forests %J IEEE Trans Computers %V C-26 %N 5 %P 425-434 %O May %K * %A Kawaguchi, E. %A Endo, T. %D 1980 %T On a method of binary-picture representation and its application to data compression %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-2 %N 1 %P 27-35 %O January %K * %A Kawaguchi, E. %A Endo, T. %A Matsunaga, J.I. %D 1983 %T Depth-first picture expression viewed from digital picture processing %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-5 %N 4 %P 373-384 %O July %K * %A Kelley, J.F. %A Chapanis, A. %D 1982 %T How professional persons keep their calendars: implications for computerization %J J Occupational Psychology %V 55 %P 241-256 %K * %A Kennedy, H.C.\0(Editor) %D 1980 %T Selected works of Guiseppe Peano %I Allen and Unwin %C Winchester, MA %A Keye, M. %D 1984 %T Technique for real time pitch period estimation %R Submitted to \fIElectronics Letters\fP %K * %A Kidd, A.L. %A Cooper, M.B. %D 1985 %T Man-machine interface issues in the construction and use of an expert system %J IJMMS %V 22 %P 91-102 %K * %A Kigger, J. %D 1984 %T The depth/breadth trade-off in the design of menu-driven user interfaces %J IJMMS %V 20 %A Klir, G.J. %A Parviz, B. %D 1985 %T General reconstruction characteristics of probabilistic and possibilistic systems %R Research Report %K * %A Knuth, D.E. %A Plass, M.F. %D 1981 %T Breaking paragraphs into lines %J Software -- Practice and Experience %V 11 %P 1119-1184 %A Knuth, D.E. %D 1983 %T The WEB system of structured documentation %R Report STAN-CS-83-980 %I Computer Science Department, University of Stanford %C Stanford, CA %A Knuth, D.E. %D 1984 %T Literate programming %J Computer J %V 27 %N 2 %P 97-111 %K * %A Kolata, G. %D 1986 %T Shakespeare's new poem: an ode to statistics %J Science %V 231 %P 335-336 %O 24 January %A Kolodner, J. %D 1983 %T Towards an understanding of the role of experience from novice to expert %J IJMMS %V 19 %K * %A Kolodner, J.L. %D 1983 %T Reconstructive memory: a computer model %J Cognitive Science %V 7 %P 281-328 %K * %A Kolodner, J.L. %D 1983 %T Maintaining organization in a dynamic long-term memory %J Cognitive Science %V 7 %P 243-280 %K * %A Konopasek, M. %A Jayaraman, S. %D 1984 %T Expert systems for personal computers: the TK!Solver approach %J Byte %P 137-156 %O May %K * %A Koontz, H. %A O'Donnell, C. %D 1972 %T Principles of management: an analysis of managerial functions %I McGraw Hill %A Korein, J. %A Badler, N. %D 1983 %T Temporal anti-aliasing in computer generated animation %J Computer Graphics %V 17 %N 3 %P 377-388 %O July %K * %A Kornfeld, W.A. %A Hewitt, C.E. %D 1981 %T The scientific community metaphor %J IEEE Trans Systems, Man and Cybernetics %V SMC-11 %N 1 %P 24-33 %O January %K * %A Kowalski, R. %D 1983 %T Logic for expert systems %J Proc Expert Systems 83 %I Churchill College %C Cambridge, England %P 80-93 %O December %K * %A Kraut, R.E. %A Hanson, S.J. %A Farber, J.M. %D 1983 %T Command use and interface design %J Proc ACM CHI 83 Human Factors in Computing Systems %P 120-123 %C Boston, MA %O December 12-15 %K * %A Klir, G.J. %D 1985 %T Architecture of systems problem solving %I Plenum Press %C New York, NY %A Kunin, J.S. %D 1982 %T Analysis and specification of office procedures %R PhD Thesis %I Department of Electrical Engineering and Computer Science, MIT %O February %A Lamb, M. %A Buckley, V. %D 1984 %T New techniques for gesture-based dialogue %J Proc 1st IFIP Conference on Human-Computer Interaction %C London, England %O 4-7 September %K * %A Langdon, G.G %D 1981 %T Tutorial on arithmetic coding %R Research Report RJ3128 %I IBM Research Laboratory %C San Jose, CA %K * %A Langdon, G.G %D 1984 %T An introduction to arithmetic coding %J IBM J Research and Development %V 28 %N 2 %P 135-149 %O March %K * %A Langdon, G.G %D 1983 %T A note on the Ziv-Lempel model for compressing individual sequences %J IEEE Trans Information Theory %V IT-30 %P 284-287 %O March %K * %A Langdon, G.G. %A Rissanen, J.J. %D 1983 %T A doubly-adaptive file compression algorithm %J IEEE Trans Communications %V COM-31 %N 11 %P 1253-1255 %O November %K * %A Langdon, G.G. %A Rissanen, J.J. %D 1982 %T A simple general binary source code %J IEEE Trans Information Theory %V IT-28 %P 800-803 %O September %A Langley, P. %D 1983 %T Learning search strategies through discrimination %J IJMMS %V 18 %P 513-541 %K * %A Lansky, A.L. %D 1985 %T Behavioral planning for multi-agent domains %R NSF Proposal %I SRI International %K * %A Latremouille, S. %A Lee, E. %D 1981 %T The design of videotex tree indexes: the use of descriptors and the enhancement of single index pages %J Telidon Behavioural Research %V 2 %I Department of Communications %O May %A Lauer, H.C. %A Needham, R.M. %D 1977 %T On the duality of operating system structures %J Operating Systems: Theory and Practice %K * %A Lazowska, E.D. %A Levy, H.M. %A Almes, G.T. %A Fischer, M.J. %A Fowler, R.J. %A Vestal, S.C. %D 1981 %T The architecture of the Eden system %J Proc Eighth Symposium on Operating System Principles %P 148-159 %C Pacific Grove, CA %O December %K * %A Lebowitz, M. %D 1980 %T Generalization and memory in an integrated understanding system %R PhD Thesis %I Yale University %C New Haven, CT %A Lebowitz, M. %D 1981 %T The nature of generalization in understanding %J Proc IJCAI 81 %P 348-353 %K * %A Lebowitz, M. %D 1983 %T Generalization from natural language text %J Cognitive Science %V 7 %P 1-40 %K * %A Lee, A. %A Lochovsky, F.H. %D 1983 %T Enhancing the usability of an office information system through direct manipulation %J Proc ACM CHI 83 Human Factors in Computing Systems %P 130-134 %C Boston, MA %O December 12-15 %K * %A Lee, D.T. %D 1982 %T On \fIk\fP-nearest neighbor Voroni diagrams in the plane %J IEEE Trans Computers %V C-31 %N 6 %P 478-487 %O June %K * %A Lee, D.T. %A Preparata, F.P. %D 1984 %T Computational geometry -- a survey %J IEEE Trans Computers %V C-33 %N 12 %P 1072-1101 %O December %A Lefebvre, V.A. %D 1977 %T The structure of awareness %I Sage Publications %C Beverly Hills, CA %O (english translation by A.Rapoport) %A Lehar, A.F. %A Stevens, R.J. %D 1984 %T High-speed manipulation of the color chromaticity of digital images %J IEEE Computer Graphics and Applications %P 34-39 %O February %K * %A Lehnert, W.G. %D 1977 %T A conceptual theory of question answering %J Proc International Joint Conference on Artificial Intelligence %I MIT %C Cambridge, MA %P 158-164 %O August %A Lehnert, W.G. %D 1978 %T The process of question answering %I Lawrence Erlbaum Associates %C Hillsdale, NJ %K * %A Lehnert, W.G. %A Dyer, M.G. %A Johnson, P.N. %A Yang, C.J. %A Harley, S. %D 1983 %T BORIS: an experiment in in-depth understanding of narratives %J Artificial Intelligence %V 20 %P 15-62 %K * %A Lemer, L. %D 1974 %T A.R.T.H.U.R. The life and opinions of a digital computer %I Harvester Press %C Sussex, England %A Lempel, A. %A Ziv, J. %T Compression of two-dimensional data %K * %A Lenat, D. %D 1983 %T The role of heuristics in learning by discovery: three case studies %B Machine learning %E R.S. Michalski, J.G. Carbonell, and T.M. Mitchell %I Tioga %P 243-306 %A Lenat, D.B. %A Sutherland, W.R. %A Gibbons, J. %D 1982 %T Heuristic search for new microcircuit structures: an application of artificial intelligence %J AI Magazine %P 17-33 %O Summer %K * %A Lenat, D.B. %D 1983 %T EURISKO: a program that learns new heuristics and domain concepts %J Artificial Intelligence %V 21 %P 61-98 %K * %A Lenat, D.B. %A Brown, J.S. %D 1984 %T Why AM and EURISKO appear to work %J Artificial Intelligence %V 23 %P 269-294 %A Lenat, D.B. %A Prakesh, M. %A Shepherd, M. %D 1986 %T CYC: using common sense knowledge to overcome brittleness and knowledge acquisition bottlenecks %J The AI Magazine %P 65-85 %O Winter %K * %A Lewis, J. %D 1963 %T Typography: basic principles. Influences and trends since the 19th century %I Van Nostrand Reinhold %C New York, NY %A Lewis, J.W. %D 1983 %T An effective graphics user interface for rules %J Proc ACM CHI 83 Human Factors in Computing Systems %P 139-143 %C Boston, MA %O December 12-15 %K * %A Lewis, J.M. %T Analysing the action of UNIX-users %D 1986 %O March %I University of Edinburgh, Department of Artificial Intelligence %K * %A Li, D.Y. %A Heath, F.G. %D 1983 %T ILEX: an intelligent relational database system %J Proc ACM Conference on Personal and Small Computers %C San Diego, CA %P 245-252 %O December %K * %A Liang, F.M. %D 1983 %T Word hy-phen-a-tion by com-put-er %R PhD Thesis %I Computer Science Department, Stanford University %C Stanford, CA %K * %A Lieberman, H. %D 1978 %T How to color in a coloring book %J Proc SIGGRAPH 78 %P 111-116 %K * %A Lieberman, H. %D 1984 %T Seeing what your programs are doing %J IJMMS %V 21 %N 4 %P 311-331 %O October %A Linde, Y. %A Buzo, A. %A Gray, R.M. %D 1980 %T An algorithm for vector quantizer design %J IEEE Trans Communications %V COM-28 %N 1 %P 84-95 %O January %K * %A Linington, P.F. %D 1983 %T Fundamentals of the layer service definitions and protocol specifications %J Proc IEEE %V 71 %N 12 %P 1341-1345 %O December %A Ljolje, A. %A Fallside, F. %D 1986 %T Synthesis of natural sounding pitch contours in isolated utterances using hidden Markov models %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-34 %N 5 %P 1074-1079 %O October %K * %A Lloyd, J.W. %D 1984 %T Foundations of logic programming %I Springer-Verlag %C Berlin %A Lowrance, R. %A Wagner, R.A. %D 1975 %T An extension of the string-to-string correction problem %J J ACM %V 22 %N 2 %P 177-183 %O April %K * %A Lozano-Perez, T. %A Wesley, M.A. %D 1979 %T An algorithm for planning collision-free paths among polyhedral obstacles %J Comm ACM %V 22 %N 10 %P 560-570 %O October %K * %A Lozano-Perez, T. %D 1981 %T Automatic planning of manipulator transfer movements %J IEEE Trans Systems, Man and Cybernetics %V SMC-11 %N 10 %P 681-698 %O October %A Lozano-Perez, T. %D 1983 %T Robot programming %J Proc IEEE %V 71 %N 7 %P 821-841 %O July %A Lu, M.I. %A Chen, C.F. %T Modified Huffman code %R Research Report %I Departmental of Electrical Engineering, Tatung Institute of Technology %C Taipei, Taiwan %K * %A Lynch, T.J. %D 1985 %T Data compression -- techniques and applications %I Lifetime Learning Publications %C Belmont, CA %A MacMillan, S.A. %D 1984 %T User models to personalize an intelligent agent %R PhD Thesis %I Stanford University %A MacQueen, J.B. %D 1967 %T Some methods for classification and analysis of multivariate observations %J Proc 5th Berkeley Symposium on Mathematical Statistics and Probability %V 1 %P 281-297 %A Maguire, M. %D 1982 %T An evaluation of published recommendations on the design of man-computer dialogues %J IJMMS %V 16 %N 3 %P 237-261 %O April %A Malcolm, M. %A Dyment, D. %D 1983 %T Experience designing the Waterloo Port user interface %J Proc ACM Conference on Personal and Small Computers %C San Diego, CA %P 168-175 %O December %K * %A Mannos, J.L. %A Sakrison, D.J. %D 1974 %T The effects of a visual fidelity criterion on the encoding of images %J IEEE Trans Information Theory %V IT-20 %N 4 %O July %K * %A Mantei, M. %D 1982 %T Disorientation behavior in person-computer interactions %R PhD Thesis %I University of Southern California %C Los Angeles, CA %A Maragos, P.A. %A Schafer, R.W. %A Mersereau, R.M. %D 1984 %T Two-dimensional linear prediction and its application to adaptive predictive coding of images %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-32 %N 6 %P 1213-1229 %O December %K * %A Marchetti, C. %D 1980 %T Society as a learning system: discovery, invention, and innovation cycles revisited %J Technological Forecasting and Social Change %V 18 %P 267-282 %K * %A Martin, T. %D 1980 %T Information retrieval %B Human interaction with computers %E Smith and Green %I Academic Press %C London, England %P 161-175 %A Maslow, A.H. %D 1954 %T Motivation and personality %I Harper and Row %C New York, NY %A Maslow, A.H. %D 1968 %T Toward a psychology of being %I Van Nostrand Reinhold %C New York, NY %O second edition %A Masrani, R. %A Keenan, T.P. %D 1984 %T Security and privacy in cellular telephone systems %J Proc AFIPS Conference on Computer Security %C Toronto, ON %O September %K * %A Masrani, R. %A Witten, I.H. %D 1984 %T Natural language processing in object-oriented Prolog %R Unpublished note %O September %A Mathews, M.V. %D 1969 %T The technology of computer music %I MIT Press %A Maturana, H.R. %D 1975 %T The organization of the living: a theory of the living organization %J IJMMS %V 7 %P 313-332 %A Maturana, H.R. %A Varela, F.J. %D 1980 %T Autopoiesis and cognition %I D. Reidel %C Dordrecht, Holland %A Mazer, M.S. %A Lochovsky, F.H. %D 1984 %T Logical routing specification in office information systems %J ACM Trans Office Information Systems %V 2 %N 4 %P 303-330 %O October %A McCarthy, J. %D 1980 %T Circumscription -- a form of non-monotonic reasoning %J Artificial Intelligence %V 13 %P 27-39 %K * %A McCracken, D.L. %A Akscyn, R.M. %D 1984 %T Experience with the ZOG human-computer interface system %J IJMMS %V 21 %N 4 %P 293-310 %O October %A McCulloch, W.S. %D 1954 %T Through the den of the metaphysician %J British J Philosophy of Science %V 5 %P 18-31 %A McDermott, J. %A Steele, B. %D 1981 %T Extending a knowledge-based system to deal with ad hoc constraints %J Proc 7th International Joint Conference on Artificial Intelligence %P 824-828 %A McDermott, J. %T Artificial intelligence meets natural stupidity %B Mind Design %E J. Haugeland %I MIT Press %C Cambridge, MA %P 143-160 %K * %A McDermott, J. %D 1982 %T A temporal logic for reasoning about processes and plans %J Cognitive Science %V 6 %P 101-155 %K * %A McDonald, D. %D 1977 %T Language generation: the linguistics component %J Proc International Joint Conference on Artificial Intelligence %P 142 %K * %A McDonald, D.D. %A Pustejovsky, J.D. %D 1985 %T Description-directed natural language generation %J Proc IJCAI %P 799-805 %K * %A McKeown, K.R. %A Wish, M. %A Matthews, K. %T Tailoring explanations for the user %J Proc IJCAI %P 794-798 %K * %A McLean, R.S. %D 1983 %T Ontario Ministry of Education specifies its microcomputer %J Proc 4th Canadian Symposium on Instructional Technology %C Winnipeg, MN %O October 19-21 %K * %A Meehan, J.R. %D 1977 %T TALESPIN, an interactive program that writes stories %J Proc 5th International Joint Conference on Artificial Intelligence %P 91-98 %A Mervis, C.B. %A Rosch, E. %D 1981 %T Categorization of natural objects %J Annual Review of Psychology %V 32 %P 89-115 %A Michaelsen, R.H. %A Michie, D. %A Boulanger, A. %D 1985 %T The technology of expert systems %J Byte %P 303-312 %O April %K * %A Miller, P.L. %D 1983 %T ATTENDING: critiquing a physician's management plan %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-5 %N 5 %P 449-461 %O September %K * %A Mitchell, T.M. %D 1982 %T Generalization as search %J Artificial Intelligence %V 18 %P 203-226 %K * %A Mitchell, T.M. %D 1983 %T Learning and problem solving %J Proc IJCAI 83 %P 1139-1151 %C Karlsruhe, W.Germany %O August %K * %A Moffat, A. %D 1986 %T Predictive text compression based on the future rather than the past %R Research Report %I Computer Science Department, University of Canterbury %C Christchurch, New Zealand %K * %A Moher, T.G. %D 1985 %T Estimating the distribution of software complexity \fIwithin\fP a program %J Proc Human Factors in Computer Systems %C San Francisco, CA %P 61-64 %O April %K * %A Mooney, R. %A de\|Jong, G. %T Learning schemata for natural language processing %J Proc IJCAI %P 681-687 %K * %A Morison, S. %D 1951 %T First principles of typography %I Cambridge University Press %C Cambridge, England %A Morrin, T.H. %D 1974 %T A black-white representation of a gray-scale picture %J IEEE Trans Computers %V C-23 %P 184-186 %O February %K * %A Morris, R. %A Cherry, L.L. %D 1975 %T Computer detection of typographical errors %J IEEE Trans Professional Communications %V PC-18 %N 1 %P 54-56 %O March %K * %A Morris, R. %A Thompson, K. %D 1979 %T Password security: a case history %J Comm ACM %V 22 %N 11 %P 594-597 %O November %K * %A Moses, J. %D 1971 %T Symbolic integration: the stormy decade %J Comm ACM %V 14 %N 8 %P 548-560 %A Mullen, J. %D 1984 %T Unlimited vocabulary speech synthesis with low data rates %J Electronics and Power %P 850-852 %O November/December %K * %A Mycielski, J. %D 1985 %T Can mathematics explain natural intelligence? %R Research Report UC-32 %I Los Alamos National Laboratory %C Los Alamos, NM %O July %K * %A Mycroft, A. %A O'Keefe, R.A. %D 1984 %T A polymorphic type system for Prolog %J Artificial Intelligence %V 23 %P 295-307 %K * %A Myers, B.A %D 1986 %T Visual programming, programming by example, and program visualization: a taxonomy %J Proc ACM CHI 86 Human Factors in Computing Systems %P 59-66 %C Boston, MA %O April 13-17 %K * %A Nagy, G. %A Wagle, S. %D 1979 %T Geographic data processing %J Computing Surveys %V 11 %N 2 %P 139-181 %O June %K * %A Nagy, G. %A Paton, K. %D 1982 %T Intelligent facsimile %P Proc Harvard Computer Graphics Week %I Graduate School of Design, Harvard University %K * %A Nagy, G. %D 1983 %T Candide's practical principles of experimental pattern recognition %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-5 %N 2 %P 199-200 %O March %K * %A Nagy, G. %D 1983 %T Optical scanning devices %J IEEE Computer %P 13-24 %O May %K * %A Nagy, G. %A Seth, S. %T Hierarchical image representation with application to optically scanned documents %R Discussion paper %K * %A Nagy, G. %D 1984 %T Advances in information extraction techniques %J Remote Sensing of Environment %V 15 %P 167-175 %K * %A Naiman, A. %D 1984 %T Some new ingredients for the cookbook approach to anti-aliased text %J Proc Graphics Interface 84 %I National Computer Graphics Association of Canada %P 99-108 %O May %A Nakatani, L.H. %A Rohrlich, J.A. %D 1983 %T Soft machines: A philosophy of user-computer interface design %J Proceedings Human Factors in Computer Systems %C Boston, MA %O December 12-15 %K * %A Nasanen, R. %D 1984 %T Visibility of halftone dot textures %J IEEE Trans Systems, Man and Cybernetics %V SMC-14 %N 6 %P 920-924 %O November/December %K * %A Nau, D.S. %D 1983 %T Expert computer systems %J IEEE Computer %V 16 %N 2 %P 63-85 %O February %A Neal, R.M. %A Lomow, G.A. %A Peterson, M.W. %A Unger, B.W. %A Witten, I.H. %D 1984 %T Inter-process communication in a distributed programming environment %J Proc Canadian Information Processing Society Conference %C Calgary, AL %P 361-364 %O May %K KConference %A Nelson, G.A. %A Pfeifer, L.L. %A Wood, R.C. %D 1972 %T High-speed octave band digital filtering %J IEEE Trans Audio and Electroacoustics %V AU-20 %P 58-65 %O March %K * %A Niblett, B.\0(Editor) %D 1980 %T Computer science and law %I Cambridge University Press %C Cambridge, England %A Nicol, R.C. %A Fenn, B.A. %A Turkington, R.D. %D 1980 %T Transmission techniques for picture viewdata %J Proc International Broadcasting Convention %K * %A Nicholson, R.T. %D 1985 %T Usage patterns in an integrated voice and data communications system %J ACM Trans Office Information Systems %V 3 %N 3 %P 307-314 %O July %A Nierstrasz, O.M. %D 1985 %T An object-oriented system %E D.Tsichritzis %B Office automation %I Springer-Verlag %C Berlin %P 167-189 %A Nilsson, N.J. %D 1986 %T Probabilistic logic %J Artificial Intelligence %V 28 %P 71-87 %A Nilsson, N.J. %D 1980 %T Principles of artificial intelligence %I Tioga %C Palo Alto, CA %A Nilsson, N.J. %D 1980 %T The interplay between experimental and theoretical methods in artificial intelligence %R Technical Note 229 %I SRI International %O September %K * %A Nilsson, N.J. %D 1981 %T Artificial intelligence: engineering, science, or slogan? %R Technical Note 248 %I SRI International %O July %K * %A Nix, R. %D 1983 %T Editing by example %R PhD Dissertation %I Computer Science Department, Yale University %C New Haven, CT %A Nix, R. %D 1984 %T Editing by example %J Proc 11th ACM Symposium on Principles of Programming Languages %C Salt Lake City, UT %P 186-195 %O January %A Noakes, P.D. %A Aish, R. %D 1984 %T A new peripheral for three-dimensional computer input %J IEEE Micro %V 4 %N 5 %P 26-35 %O October %A Nooteboom, S.G. %D 1983 %T The temporal organization of speech and the process of spoken-word recognition %J IPO Annual Progress Report %V 18 %P 32-36 %K * %A Norman, D.A. %D 1981 %T The trouble about Unix %J Datamation %V 27 %N 12 %P 139-150 %A Norman, D.A. %D 1984 %T Stages and levels in human-machine interaction %J IJMMS %V 21 %N 4 %P 365-375 %O October %A Norman, D.A. %A Draper, S.W.\0(Editors) %D 1986 %T User centered system design \(em new perspectives on human-computer interaction %I Lawrence Erlbaum Associates %C Hillsdale, NJ %A Norman, K.L. %A Weldon, L.J. %A Shneiderman, B. %D 1985 %T Cognitive representations of windows and multiple screen layouts of computer interfaces %R Research Report CAR-TR-123, CS-TR-1498 %I Computer Science Department, University of Maryland %O May %A Null, A. %D 1971 %T Space-filling curves or how to waste time with a plotter %J Software -- Practice and Experience %V 1 %P 403-410 %K * %A Oren, T.I. %A Brzozowski, J.A. %A Gilmore, P.C. %D 1982 %T Crisis in Canadian academic Computer Science: facts and recommendations %R Report prepared by the Executive Committee of Canadian Computer Science Departments Chairmen %O January %A O'Shea, T. %A Self, J. %D 1983 %T Learning and teaching with computers: artificial intelligence in education %I Prentice-Hall %C Englewood Cliffs, NJ %A O'Shea, T. %A Eisenstadt, M.\0(Editors) %D 1984 %T Artificial intelligence: tools, techniques, and applications %I Harper and Row %C New York, NY %A Ogawa, Y. %A Shima, K. %A Sugawara, T. %A Takagi, S. %D 1984 %T Knowledge representation and inference environment: KRINE -- an approach to integration of frame, Prolog and graphics %J Proc International Conference on Fifth Generation Computer Systems %I ICOT %P 643-651 %K * %A Pake, G.E. %D 1985 %T Research at Xerox PARC: a founder's assessment %J IEEE Spectrum %V 22 %N 10 %P 54-61 %O October %A Paliwal, K.K. %A Espeland, O. %D 1983 %T Some considerations about the shape of the window filter in an adaptive gradient lattice algorithm %R Report %I Division of Telecommunications, University of Trondheim %C Trondheim-NTH, Norway %K * %A Papamichalis, P.E. %D 1985 %T Markov-Huffman coding of LPC parameters %J IEEE Trans Acoustics, Speech and Signal Processing %C ASSP-33 %N 2 %P 451-453 %O April %A Park, O.C. %A Tennyson, R.D. %D 1983 %T Computer-based instructional systems for adaptive education: a review %J Contemporary Education Review %V 2 %N 2 %P 121-135 %O Fall %K * %A Parker-Rhodes, A.F. %D 1978 %T Inferential semantics %I Harvester Press %C Brighton, Sussex %A Patil, R.S. %A Szolovits, P. %A Schwartz, W.B. %D 1981 %T Causal understanding of patient illness in medical diagnosis %J Proc 7th International Joint Conference on Artificial Intelligence %P 893-899 %K * %A Patil, R.S. %A Szolovits, P. %A Schwartz, W.B. %T Information acquisition in diagnosis %J Proc International Joint Conference on Artificial Intelligence %P 345-348 %K * %A Patten, T. %D 1986 %T Interpreting systemic grammar as a computational representation: a problem solving approach to text generation %R PhD Thesis %I University of Edinburgh %A Paulus, E. %D 1980 %T The concept of the NN-error risk with respect to an arbitrary separating surface and its applications to clustering %J Proc IEEE Conference %K * %A Pavlidis, T. %D 1981 %T Contour filling in raster graphics %J ACM Computer Graphics %V 15 %N 3 %P 29-36 %O August %K * %A Pawlak, Z. %D 1982 %T Rough sets %J Int J Computer and Information Systems %V 11 %N 5 %P 341-356 %K * %A Pawlak, Z. %D 1985 %T Rough sets and fuzzy sets %J Fuzzy Sets and Systems %P 99-103 %K * %A Paxton, A.L %A Turner, E.J. %D 1984 %T The application of human factors to the needs of the novice computer user %J IJMMS %V 20 %N 2 %P 137-156 %O February %A Peano, G. %D 1890 %T Sur une courbe, qui remplit toute une aire plane %J Math Annalen %V 36 %P 157-160 %K * %A Pearl, J. %D 1985 %T Fusion, propagation, and structuring in Bayesian networks %R Technical Report CSD-850022 R-42, Revision I %I Cognitive Systems Laboratory, Computer Science Department, UCLA %O June %A Pearl, J. %D 1986 %T Fusion, propagation, and structuring in belief networks %J Artificial Intelligence %V 29 %N 3 %P 241-288 %O September %A Peng, X.T. %A Tu, X.C. %A Wang, P.Z. %D 1986 %T Studies on parametric fuzzy controllers %R Research Report %K * %A Pereiro, L.M. %A Nagr, R. %D 1984 %T Delta-Prolog: a distributed logic programming language %R Submitted to Int Conference on 5th Generation Systems %C Tokyo, Japan %O November %K * %A Perlman, G. %D 1981 %T Two papers in cognitive engineering: The design of an interface to a programming system, and MENUNIX: a menu-based interface to Unix (user manual) %R Research Report 8105 %I Center for Human Information Processing, University of California %C San Diego, CA %O November %K * %A Perlman, G. %D 1984 %T Natural artificial languages: low-level processes %J IJMMS %V 20 %N 4 %P 373-419 %O April %A Perry, T.S. %A Wallich, P. %D 1985 %T Inside the PARC: the `information architects' %J IEEE Spectrum %V 22 %N 10 %P 62-75 %O October %A Peters, A.M. %D 1983 %T The units of language acquisition %I Cambridge University Press %C Cambridge, England %A Phillips, J. %D 1983 %T Self-describing programming environments %R PhD Thesis %I Computer Science Department, Stanford University %C Stanford, CA %A Pierce, J.R. %D 1962 %T Symbols, signals and noise %I Hutchinson %C London, England %A Pike, R. %D 1983 %T Graphics in overlapping bitmap layers %J ACM Trans Graphics %V 2 %N 2 %P 135-160 %O April %K * %A Poggio, A. %A Garcia Luna Aceves, J.J. %A Craighill, E.J. %A Moran, D. %A Aguilar, L. %A Worthington, D. %A Hight, J. %D 1985 %T CCWS: a computer-based, multimedia information system %J IEEE Computer %V 18 %N 10 %P 92-103 %O October %A Poritz, A.B. %D 1982 %T Linear predictive hidden Markov models and the speech signal %J Proc %P 1291-1294 %K * %A Post, E. %D 1983 %T Real programmers don't use Pascal %J Datamation %P 263-265 %O July %K * %A Postel, J.B. %D 1980 %T Internetwork protocol approaches %J IEEE Trans Communications %V COM-28 %N 4 %P 604-611 %O April %K * %A Potmesil, M. %A Chakravarty, I. %D 1983 %T Modeling motion blur in computer-generated images %J ACM Computer Graphics %V 17 %N 3 %P 389-399 %O July %K * %A Poulton, A.S. %D 1983 %T Microcomputer speech synthesis and recognition %I Sigma Technical Press %C Wilmslow, Cheshire, UK %A Preucil, M. %A Sebela, Z. %D 1982 %T Computer-assisted simulation of a coal-mine winding system %J Proc 4th Formator Symposium on Mathematical Methods for the Analysis of Large-scale Systems %C Prague %P 391-404 %O May 18-21 %K * %A Prusinkiewicz, P. %A Christopher, M. %D 1984 %T Hologram-like transmission of pictures %R Technical Report CS-84-17 %I Computer Science Department, University of Regina %O November %K * %A Purvy, R. %A Farrell, J. %A Klose, P. %D 1983 %T The design of Star's records processing: data processing for the noncomputer professional %J ACM Trans Office Information Systems %V 1 %N 1 %P 3-24 %O January %K * %A Quinlan, J.R. %D 1983 %T Inferno: a cautious approach to uncertain inference %J Computer J %V 26 %N 3 %P 255-269 %A Qureshi, S.U.H. %D 1985 %T Adaptive equalization %J Proc IEEE %V 73 %N 9 %P 1349-1387 %O September %A Rabiner, L.R. %A Crochiere, R.E. %D 1975 %T A novel implementation for narrow-band FIR digital filters %J IEEE Trans Acoustics, Speech and Signal Processing %V ASSP-23 %N 5 %P 457-464 %O October %K * %A Radhakrishnan, T. %A Grossner, C.P. %D 1985 %T Cuenet \(em a distributed computing facility %J IEEE Micro %P 42-52 %O February %K * %A Raeder, G. %D 1985 %T A survey of current graphical programming techniques %J IEEE Computer %V 18 %N 8 %P 11-25 %A Rashid, R.F. %D 1980 %T An interprocess communication facility for Unix %R Technical Report %I Computer Science Department, Carnegie-Mellon University %O February %A Rashid, R.F. %A Robertson, G.G. %D 1981 %T Accent: a communication oriented network operating system kernel %J Proc Eighth Symposium on Operating System Principles %P 64-75 %C Pacific Grove, CA %O December %A Rasmussen, J. %D 1983 %T Skills, rules, and knowledge; signals, signs, and symbols, and other distinctions in human performance models %J IEEE Trans Systems, Man and Cybernetics %V SMC-13 %N 3 %P 257-266 %O May/June %K * %A Rassbach, M.E. %D 1980 %T CLASSY: an adaptive clustering algorithm %J Proc IEEE Conference %P 442-444 %K * %A Rawlings, C. %A Fox, J. %D 1983 %T The UNIT package -- a critical appraisal of a frame-based knowledge representation system %J Proc Expert Systems 83 %I Churchill College %C Cambridge, England %P 15-29 %O December %K * %A Redell, D.D. %A White, J.E. %D 1983 %T Interconnecting electronic mail systems %J IEEE Computer %V 16 %N 9 %P 55-63 %O September %K * %A Reeds, J.A. %A Weinberger, P.J. %D 1984 %T File security and the UNIX system \fIcrypt\fP command %J Bell System Technical J %V 63 %N 8, part 2 %P 1673-1684 %O October %A Reichardt, J. %D 1971 %T The computer in art %I Studio Vista %C London, England %A Reiter, R. %D 1980 %T A logic for default reasoning %J Artificial Intelligence %V 13 %P 81-132 %K * %A Reynolds, J.K. %A Postel, J.B. %A Katz, A.R. %A Finn, G.C. %A DeSchon, A.L. %D 1985 %T The DARPA experimental multimedia mail system %J IEEE Computer %V 18 %N 10 %P 82-89 %O October %A Rich, C. %D 1982 %T Knowledge representation languages and predicate calculus: how to have your cake and eat it too %J Proc National Conference on Artificial Intelligence %P 193-196 %A Rich, E. %D 1984 %T The gradual expansion of artificial intelligence %J IEEE Computer %V 17 %N 5 %P 4-12 %O May %A Riesbeck, C.K. %D 1975 %T Conceptual analysis %B Conceptual information processing %E R.C.Schank %I North Holland %C Amsterdam %A Riesbeck, C.K. %D 1981 %T Failure-driven reminding for incremental learning %J Proc IJCAI 81 %P 115-120 %K * %A Riesbeck, C.K. %D 1982 %T Realistic language comprehension %E W.G. Lenhert and M.H. Ringle %B Strategies for natural language processing %I Lawrence Erlbaum Associates %P 37-54 %A Riesbeck, C.K. %D 1984 %T Knowledge reorganization and reasoning style %J IJMMS %V 20 %P 45-61 %K * %A Rissanen, J. %A Langdon, G.G. %T Arithmetic coding %J IBM J Research and Development %D 1979 %V 23 %N 2 %P 149-162 %O March %K * %A Rissanen, J. %D 1984 %T Complexity of strings in the class of Markov sources %R Research Report %I IBM Research Laboratory %C San Jose, CA %K * %A Rissanen, J. %D 1986 %T Stochastic complexity and sufficient statistics %R Research Report %A Ritchie, D.M. %D 1981 %T On the security of UNIX %R Programmers Manual for UNIX System III Volume II: Supplementary Documents %I Western Electric Corporation %A Ritchie, G.D. %A Hanna, F.K. %D 1984 %T AM: a case study in AI methodology %J Artificial Intelligence %V 23 %P 249-268 %K * %A Roberts, M.G. %D 1982 %T Local order estimating Markovian analysis for noiseless source coding and authorship identification %R PhD Thesis %I Stanford University %A Rogers, H. %D 1943 %T Paragraphs on printing %I William E. Rudges %C New York, NY %O re-published by Dover Publications, New York, 1979 %A Rosenthal, D.S.H. %D 1982 %T Managing graphical resources %J Computer Graphics %V 16 %N 4 %P 38-45 %O December %K * %A Ross, P. %A Jones, J. %A Millington, M. %D 1985 %T User modelling in command-driven computer systems %R DAI Research Paper No 264 %I Department of Artificial Intelligence, University of Edinburgh %K * %A Rouse, S.H. %A Rouse, W.B. %D 1980 %T Computer-based manuals for procedural information %J IEEE Trans Systems, Man and Cybernetics %V SMC-10 %N 8 %P 506-510 %O August %A Rowe, N.C. %D 1984 %T Modelling degrees of item interest for a general database query system %J IJMMS %V 20 %N 5 %P 421-443 %O May %K * %A Runciman, C. %A Thimbleby, H. %D 1986 %T Equal opportunity interactive systems %R Report %I Computer Science Department, University of York %C York %K * %A Rychener, M.D. %D 1979 %T A semantic network of production rules in a system for describing computer structures %J Proc 6th Joint Conference on Artificial Intelligence %P 738-743 %A Ryman, R. %A Singh, B. %D 1982 %T The Benesh notation computerized editor %J Proc Dance in Canada Conference %O June %A Sagan, H. %D 1986 %T Approximating polygons for Lebesgue's and Schoenberg's space filling curves %J American Mathematical Monthly %P 361-368 %O May %K * %A Sammut, C. %A Banerji, R. %D 1983 %T Hierarchical memories: an aid to concept learning %J Proc International Machine Learning Workshop %P 74-80 %I Allerton House %C Monticello, IL %O June 22-24 %K * %A Sammut, C. %A Banerji, R. %D 1986 %T Learning concepts by asking questions %B Machine learning Volume 2 %E R.S. Michalski, J.G. Carbonell, and T.M. Mitchell %I Morgan Kaufmann Inc %C Los Altos, CA %P 167-191 %K * %A Sandewall, E. %T A functional approach to non-monotonic logic %J Proc IJCAI %P 100-106 %K * %A Sakata, S. %A Ueda, T. %D 1985 %T A distributed interoffice mail system %J IEEE Computer %V 18 %N 10 %P 106-116 %O October %A Samet, H. %D 1983 %T A quadtree medial axis transform %J Comm ACM %V 26 %N 9 %P 680-693 %O September %A Samet, H. %D 1984 %T The quadtree and related hierarchical data structures %J Computing Surveys %V 16 %N 4 %P 187-260 %O June %A Santisteban, A. %D 1983 %T The perceptual color space of digital image display terminals %J IBM J Research and Development %V 27 %N 2 %P 127-132 %O March %K * %A Sawaragi, Y. %A Yoshikawa %D 1970 %T Discrete-time markovian decision processes with incomplete state observation %J The Annals of Mathematical Statistics %V 41 %N 1 %P 78-86 %K * %A Schank, R.C.\0(Editor) %D 1975 %T Conceptual information processing %I North Holland %A Schank, R.C. %A Abelson, R. %D 1977 %T Scripts, plans, goals and understanding %I Lawrence Erlbaum Associates %A Schank, R.C. %D 1980 %T Language and memory %J Cognitive Science %V 4 %P 243-284 %K * %A Schank, R.C. %A Slade, S. %T Advisory systems %K * %A Scharf, T.F. %D 1984 %T Sounding out speech synthesis %J Electronics and Power %P 847-849 %O November/December %K * %A Schroeder, M.E. %D 1969 %T Images from computers %J IEEE Spectrum %O March %K * %A Schulert, A.J. %A Rogers, G.T. %A Hamilton, J.A. %D 1985 %T ADM \(em a dialog manager %J Proc ACM CHI 85 Human Factors in Computing Systems %P 177-183 %O April %K * %A Searle, J.R. %D 1980 %T Minds, brains, and programs %J Behavioral and Brain Sciences %V 3 %P 417-457 %K * %A Searle, J.R. %D 1983 %T Intentionality %I Cambridge University Press %C Cambridge, England %A Seely\|Brown, J. %A Burton, R.R. %A Bell, A.G. %D 1975 %T SOPHIE \(em a step toward creating a reactive learning environment %J IJMMS %V 7 %N 5 %P 675-696 %O September %A Segre, A.M. %A Sherwood, B.A. %A Dickerson, W.B. %D 1983 %T An expert system for the production of phoneme strings from unmarked english text using machine induced rules %J Proc Association for Computational Linguistics %C Pisa, Italy %O September %K * %A Selim, S.Z. %A Ismail, M.A. %D 1984 %T \fIK\fP-means-type algorithms: a generalized convergence theorem and characterization of local optimality %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-6 %N 1 %P 81-87 %O January %K * %A Sergot, M. %T Prospects for representing the law as logic programs %B In Clark and Tarnlund's book %K * %A Seybold %D 1985 %T Apple Laserwriter %J Seybold Report on Publishing Systems %V 14 %N 9 %O January 28 %K * %A Shafer, G. %D 1976 %T A mathematical theory of evidence %I Princeton University Press %C Princeton, NJ %A Shamos, M.I. %A Hoey, D. %D 1975 %T Closest-point problems %J Proc 16th IEEE Symposium on Foundations of Computer Science %P 151-162 %O October %A Shamos, M.I. %D 1977 %T Computational geometry %I Springer-Verlag %C New York, NY %A Shannon, C.E. %D 1948 %T A mathematical theory of communication %J Bell System Technical J %V 27 %P 398-403 %O July %A Shannon, C.E. %D 1951 %T Presentation of a maze-solving machine %B Trans 8th Conference Josiah Macy Foundation %E H.von Foerster %C New York, NY %P 173-192 %A Shannon, C.E. %D 1951 %T Prediction and entropy of printed English %J Bell System Technical J %P 50-64 %O January %K * %A Shapiro, E. %D 1983 %T A subset of concurrent Prolog and its interpreter %R ICOT Technical Report TR-003 %O January %K * %A Shapiro, E. %D 1983 %T Systems programming in concurrent Prolog %R ICOT Technical Report TR-034 %O November %K * %A Shapiro, E. %A Takeuchi, A. %D 1983 %T Object oriented programming in concurrent Prolog %J New Generation Computing %V 1 %P 25-48 %K * %A Shaw, M.L.G. %A Gaines, B.R. %D 1983 %T Does the human component in the network have a protocol? %J Proc International Electrical, Electronics Conference %V 2 %P 546-549 %C Toronto, ON %O September 26-28 %K * %A Shaw, M.L.G %A Gaines, B.R. %D 1985 %T Knowledge engineering tools for expert systems %B Computer models for decision making %E G.Mitra %I North Holland %C Amsterdam %K * %A Shneiderman, B. %D 1984 %T Response time and display rate in human performance with computers %J Computing Surveys %V 16 %N 3 %P 265-285 %O September %A Shneiderman, B. %A Norman, K. %A Rogers, J. %A Arifin, R. %A Weldon, L. %D 1985 %T A multi-screen programmer work station based on the IBM PC %R Research Report %I Computer Science Department, University of Maryland %O April %A Shoch, J.F. %A Hupp, J.A. %D 1982 %T The `worm' programs \(em early experience with a distributed computation %J Comm ACM %V 25 %N 3 %P 172-180 %O March %K * %A Shoemake, K. %T Animating rotation with quaternion curves %D 1985 %J ACM %V 19 %N 3 %O July %K * %A Shortliffe, E.H. %A Buchanan, B.G. %D 1975 %T A model of inexact reasoning in medicine %J Mathematical Biosciences %V 23 %P 351-379 %A Shortliffe, E.H. %D 1976 %T Computer-based medical consultations: MYCIN %I Elsevier Science %C New York, NY %K * %A Shortliffe, E.H. %D 1980 %T Consultation systems for physicians: the role of artificial intelligence techniques %J Proc Canadian Society for Computational Studies of Intelligence %I University of Victoria %C Victoria, BC %K * %A Shrager, J.C. %T Invoking a beginner's aid process by recognizing DCL goals %D 1981 %R MSc Thesis %I University of Pennsylvania %A Shrager, J.C. %A Finin, T. %D 1982 %T An expert system that volunteers advice %J Proc National Conference on Artificial Intelligence %P 339-340 %K * %K * %A Shu, C.S. %D 1985 %T FORMAL: A forms-oriented visual-directed application development system %J IEEE Computer %V 18 %N 8 %P 38-49 %A Sierpinski, W. %D 1912 %T Sur une nouvelle courbe qui remplit toute une aire plaine %J Bull Acad Sci Cracovie %V Serie A %P 462-478 %K * %A Simons, G.L. %D 1980 %T Robots in industry %I National Computing Centre %C Manchester, England %A Simpson, R.J. %A Terrell, T.J. %D 1984 %T Digital filtering using the NEC PD7720 signal processor %J Microprocessing and Microprogramming %V 14 %P 67-78 %K * %A Sleeman, D. %D 1982 %T Assessing aspects of competence in basic algebra %B Intelligent Tutoring Systems %E D. Sleeman and J.S. Brown %I Academic Press %C London, England %P 185-200 %A Sloman, A. %A Croucher, M. %D 1981 %T Why robots will have emotions %J Proc 7th International Joint Conference on Artificial Intelligence %V 1 %P 197-202 %C Vancouver, BC %K * %A Smith, K. %D 1985 %T Watch out hackers, public encryption chips are coming %J Electronics Week %P 30-31 %O May 20 %K * %A Smith, R. %D 1979 %T Tint fill %J Proc ACM Conference %P 276-284 %K * %A Solomon, H. %D 1977 %T Data dependent clustering techniques %B Classification and clustering %E J. Van Ryzin %I Academic Press %C New York, NY %P 155-173 %A Southall, R. %D 1984 %T First principles of typographic design for document production %J TUGBOAT (TEX Users Group Newsletter) %V 5 %N 2 %P 79-90 %K * %A Sowa, J.F. %D 1983 %T Generating language from conceptual graphs %B Computational Linguistics %E N.Cercone %P 29-43 %I Pergamon %C Oxford, England %K * %A Sparck\|Jones, K. %D 1984 %T User models and expert systems %R Technical Report %I Computer Laboratory, University of Cambridge %C Cambridge, England %K * %A Spector, A.Z. %D 1982 %T Performing remote operations efficiently on a local computer network %J Comm ACM %V 25 %N 4 %P 246-260 %O April %K * %A Spencer, H. %D 1969 %T The visible word %I Lund Humphries %C London, England %A Stankovic, J.A. %D 1982 %T Software communication mechanisms: procedure call versus messages %J IEEE Computer %P 19-25 %O April %K * %A Stankovic, J.A. %D 1984 %T A perspective on distributed computer systems %J IEEE Trans Computers %V C-33 %N 12 %P 1102-1115 %O December %K * %A Staunstrup, J. %D 1982 %T Message passing communication versus procedure call communication %J Software -- Practice and Experience %V 12 %P 223-234 %K * %A Stefik, M. %D 1979 %T An examination of a frame-structured representation system %J Proc 6th International Conference on Artificial Intelligence %P 265-270 %K * %A Stefik, M. %A Conway, L. %D 1982 %T Towards the principled engineering of knowledge %J AI Magazine %P 4-16 %O Summer %K * %A Stefik, M. %A Bobrow, D.G. %A Mittal, S. %A Conway, L. %D 1983 %T Knowledge programming in LOOPS: report on an experimental course %J AI Magazine %P 3-13 %O Fall %K * %A Stefik, M.J. %A Bobrow, D.G. %A Kahn, K.M. %D 1986 %T Integrating access-oriented programming into a multiparadigm environment %J IEEE Software %P 10-18 %O January %K * %A Stefik, M.J. %A Bobrow, D.G. %D 1986 %T Object-oriented programming: themes and variations %J AI Magazine %V 6 %N 4 %P 40-62 %O Winter %A Stevens, M.E. %A Little, J.L. %D 1967 %T Automatic typographic-quality typesetting techniques: a state-of-the-art review %I National Bureau of Standards %A Stevens, R.J. %A Lehar, A.F. %A Preston, F.H. %D 1983 %T Manipulation and presentation of multi-dimensional image data using the Peano scan %J IEEE Trans Pattern Analysis and Machine Intelligence %P 520- %O September %A Stoffel, J.G. %A Moreland, J.F. %D 1981 %T A survey of electronic techniques for pictorial image reproduction %J IEEE Trans Communications %V COM-17 %N 12 %P 1898-1925 %O December %K * %A Stroustrup, B. %D 1984 %T The C++ programming language %R Computing Science Technical Report 108 %I Bell Laboratories %C Murray Hill, NJ %O January %K * %A Stroustrup, B. %D 1984 %T Data abstraction in C %R Computing Science Technical Report 109 %I Bell Laboratories %C Murray Hill, NJ %O January %K * %A Suchman, L.A. %D 1982 %T Toward a sociology of human-machine interaction: pragmatics of instruction-following %R Working Paper %I Xerox PARC, (Intelligent Systems Laboratory) %C Palo Alto, CA %A Suchman, L.A. %D 1982 %T Human-machine interaction and the idea of a self-explanatory machine %J Paper presented at the Annual Meeting of the American Anthropological Society %C Washington, DC %O December %K * %A Suchman, L.A. %D 1983 %T The role of common sense in interface design %B Office Automation: Jekyll or Hyde %E D.Marschall and J.Gregory %I Working Women Education Fund %C Cleveland, OH %P 96-102 %A Suchman, L.A. %D 1983 %T Office procedure as practical action: models of work and system design %J ACM Trans Office Information Systems %V 1 %N 4 %P 320-328 %O October %K * %A Suchman, L.A. %D 1985 %T Plans and situated actions: the problem of human-machine communication %R PhD Thesis %I Xerox PARC %C Palo Alto, CA %K * %A Sugeno, M. %A Nishida, M. %D 1984 %T Fuzzy control of model car %K * %A Summers, P.D. %A Grossman, D.D. %D 1984 %T XPROBE: an experimental system for programming robots by example %J Int J Robotics Research %V 3 %N 1 %P 25-39 %O Spring %A Sussman, G.J. %D 1975 %T A computer model of skill acquisition %I American Elsevier %C New York, NY %A Tannenbaum, A. %T Political history of UNIX %R Report %I MASSCOMP %C Westford, MA %K * %A Tanner, W. %D 1979 %T Industrial robots -- Volume 1: Fundamentals %I Society of Manufacturing Engineers %C Dearborn, MI %A Tennant, H.R. %A Ross, K.M. %A Thompson, C.W. %D 1983 %T Usable natural language interfaces through menu-based natural language understanding %J Proc ACM CHI 83 Human Factors in Computing Systems %P 154-160 %C Boston, MA %O December 12-15 %K * %A Test, J.A. %D 1982 %T The NUnix window system %R Internal Report %I Laboratory for Computer Science, MIT %C Cambridge, MA %K * %A Thimbleby, H. %D 1980 %T Dialogue determination %J IJMMS %V 13 %N 3 %P 295-304 %O October %A Thomsett, R. %D 1980 %T People and project management %I Yourden Press %C New York, NY %A Thompson, B.A. %A Thompson, W.A. %D 1985 %T Inside an expert system %J Byte %P 315-330 %O April %K * %A Thompson, K. %D 1984 %T Reflections on trusting trust %J Comm ACM %V 27 %N 8 %P 761-763 %O August %A Ting, D. %A Prasada, B. %D 1980 %T Digital processing techniques for encoding of graphics %J Proc IEEE %V 68 %N 7 %P 757-769 %O July %A Tokuda, H. %A Manning, E.G. %D 1983 %T An interprocess communication model for a distributed software testbed %J Proc ACM SIGCOMM 83 %I University of Texas %C Austin, TX %O March %K * %A Tokuda, H. %A Radia, S.P. %A Manning. E.G. %D 1983 %T Shoshin OS: a message-based operating system for a distributed software testbed %J Proc 16th Annual Hawaii International Conference on System Sciences %P 329-338 %K * %A Tou, I.T. %A Gonzalez, R.C. %D 1974 %T Pattern recognition principles %I Addison-Wesley %C Reading, MA %A Truin, P.G.M. %D 1983 %T The `speaking tablet' as an aid in the acquisition of reading skills by dyslexic children %J IPO Annual Progress Report %V 18 %P 79-84 %K * %A Tsichritzis, D.\0(Editor) %D 1983 %T Beta Gamma %R Technical Report CSRG-150 %I Computer Systems Research Group, University of Toronto %C Toronto, ON %A Tsichritzis, D. %D 1985 %T Objectworld %E D.Tsichritzis %B Office automation %I Springer-Verlag %C Berlin %P 379-398 %A Turkle, S. %D 1982 %T The subjective computer: a study in the psychology of personal computation %J Social Studies of Science %V 12 %N 2 %P 173-205 %K * %A Tyree, A. %D 1986 %T Expert systems and the law %J Current Affairs Bulletin %P 13-18 %P March %K * %A Ulichney, R.A. %A Troxel, D.E. %D 1982 %T Scaling binary images with the telescoping template %J IEEE Trans Pattern Analysis and Machine Intelligence %V PAMI-4 %N 3 %P 331-335 %A Umphress, D. %A Williams, G. %D 1985 %T Identity verification through keyboard characteristics %J IJMMS (submitted) %K * %A Unger, B. %A Birtwistle, G. %A Cleary, J. %A Hill, D. %A Lomow, G. %A Neal, R. %A Peterson, M. %A Witten, I.H. %A Wyvill, B. %D 1984 %T Jade: a simulation and software prototyping environment %J Proc Conference on Simulation in Strongly Typed Languages %C San Diego, CA %O February %K KConference %A Unger, B.W. %A Lomow, G.A. %A Birtwistle, G.. %D 1984 %T Simulation software and Ada %I Society for Computer Simulation %K * %A University\|of\|Chicago\|Press %D 1969 %T A manual of style %I University of Chicago %A Uribe, R.B. %D 1981 %T Modeling autopoiesis %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 51-62 %A Van\|Dijk, T.A. %A Kintsch, W. %D 1983 %T Strategies of discourse comprehension %I Academic Press %C New York, NY %A Van\|Lehn, K. %D 1983 %T Felicity conditions for human skill acquisition: validating an AI-based theory %R Research Report CIS-21 %I Xerox PARC %C Palo Alto, CA %O November %A Varela, F.J. %A Maturana, H.R. %A Uribe, R.B. %D 1974 %T Autopoiesis: the organization of living systems, its characterization and a model %J Biosystems %V 5 %P 187-196 %A Varela, F.J. %D 1979 %T Principles of biological autonomy %I North Holland %C New York, NY %A Varela, F.J. %D 1981 %T Describing the logic of the living %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 36-48 %A Wade, N. %D 1985 %T Literal pictures %J Word and Image %V 1 %N 3 %P 242-272 %O July-September %K * %A Wagner, R.A. %A Fischer, M.J. %D 1974 %T The string-to-string correction problem %J J ACM %V 21 %N 1 %P 168-173 %O January %K * %A Wall, R.S. %A Apon, A.W. %A Beal, J. %A Gately M.T. %A Oren, L.G. %D 1985 %T An evaluation of commercial expert system building tools %R Computer Science Laboratory Technical Report 85-30 %I Texas Instruments %C Dallas, TX %O November %K * %A Waltz, D.L. %D 1975 %T Natural language access to a large data base %J Advance papers of the International Joint Conference on Artificial Intelligence %I MIT %C Cambridge, MA %A Waterman, D.A. %D 1978 %T A rule-based approach to knowledge acquisition for man-machine interface programs %J IJMMS %V 10 %P 693-711 %K * %A Waters, R.C. %D 1985 %T The programmer's apprentice: a session with KBEmacs %J IEEE Trans Software Engineering %V SE-11 %N 11 %P 1296-1320 %O November %K * %A Webber, B.L. %A Nilsson, N.J. %D 1981 %T Readings in artificial intelligence %I Tioga %C Palo Alto, CA %A Weinreb, D. %A Moon, D. %D 1981 %T LISP machine manual %I Third edition %O March %A Weizenbaum, J. %D 1976 %T Computer power and human reason %I Freeman %C San Francisco, CA %A Welch, T.A. %D 1984 %T A technique for high-performance data compression %J IEEE Computer %V 17 %N 6 %P 8-19 %O June %K * %A Weyer, S.A. %D 1982 %T Searching for information in a dynamic book %R PhD Thesis %I School of Education, Stanford University %O (Also Report SCG-82-1, Xerox Parc) %A Whalen, T. %A Mason, C. %D 1981 %T The use of tree-structured index which contains three types of design defects %J Telidon Behavioural Research %V 2 %I Department of Communications %O May %A Whalen, T. %A Latremouille, S. %D 1981 %T The effectiveness of a tree-structured index when the existence of information is uncertain %J Telidon Behavioural Research %V 2 %I Department of Communications %O May %A Wijk, C.van %A Kempen, G. %D 1985 %T From sentence structure to intonation contour %E B.S.Muller %T Sprachsynthese: zur Synthese von naturlich gesprochener Sprache aus Texten und Konzepten %I Georg Olms Verlag %C Hildesheim %K * %A Wilensky, R. %A Arens, Y. %A Chin, D. %D 1984 %T Talking to Unix in English: an overview of UC %J Comm ACM %V 27 %N 6 %P 574-593 %O June %K * %A Wilkes, A.J. %A Singer, D.W. %A Gibbons, J.J. %A King, T.R. %A Robinson, P. %A Wiseman, N.E. %D 1984 %T The Rainbow workstation %J Computer J %V 27 %N 2 %O May %K * %A Wilkes, A.J. %A Wiseman, N.E. %D 1982 %T A soft-edged character set and its derivation %J Computer J %V 25 %N 1 %P 140-147 %O February %K * %A Wilkinson, W. %D 1980 %T Viewdata: The Prestel System %B Videotext: the coming revolution in home/office information retrieval %E Sigel, E. %I Harmony Books %C New York, NY %P 57-86 %A Wilks, Y. %D 1977 %T Good and bad arguments about semantic primitives %R Research Report %I Department of Artificial Intelligence, University of Edinburgh %O May %K * %A Wilks, Y. %D 1984 %T Beliefs, points of view and multiple environments %B Artificial and human intelligence %E A.Elithorn and R.Banerji %I Elsevier Science %P 147-171 %K * %A Willems, NJ %D 1983 %T STEP: A model of standard English intonation patterns %J IPO Annual Progress Report %V 18 %P 37-42 %K * %A Williams, G. %D 1984 %T The Apple Macintosh computer %J Byte %V 9 %N 2 %P 30-54 %O February %A Winograd, T. %D 1972 %T Understanding natural language %I Academic Press %C New York, NY %A Winograd, T. %D 1984 %T Moving the semantic fulcrum %R Report CSLI-84-18 %I Center for the study of language and information, Stanford University %C Stanford, CA %O December %K * %A Witten, I.H. %D 1983 %T The Department of Computer Science, University of Calgary %J Computer Science Association Newsletter %V 11 %N 1 %P 15-23 %O December %K KArticle %A Witten, I.H. %A Cleary, J.G. %D 1986 %T Foretelling the future by adaptive modeling %J Abacus %V 3 %N 3 %P 16-36 %O Spring %K KArticle %A Witten, I.H. %A Fremont, D. %D 1984 %T A student information service for a University Computer Science department %J Proc 15th Ontario Universities Computing Conference %I Lakehead University %C Thunder Bay, ON %O June %K KInvited %A Witten, I.H. %D 1985 %T Elements of computer typography %J IJMMS %V 23 %N 6 %P 623-687 %O December %K KJournal %A Witten, I.H. %A Bramwell, B. %D 1985 %T A system for interactive viewing of structured documents %J Comm ACM %V 28 %N 3 %P 280-288 %O March %K KJournal %A Witten, I.H. %D 1984 %T Dynamic documents %J Proc PROTEXT I -- First International Conference on Text Processing Systems %I Boole Press %C Dublin, Ireland %P 234-239 %O October %K KConference %A Witten, I.H. %A Greenberg, S. %D 1985 %T User interfaces for office systems %B Oxford Surveys in Information Technology Volume 2 %E P.I. Zorkoczy %I Oxford University Press %C Oxford, England %P 69-104 %K KJournal %A Witten, I.H. %D 1986 %T Making computers talk \(em an introduction to speech synthesis %I Prentice-Hall %C Englewood Cliffs, NJ %A Witten, I.H. %D 1987 %T Computer speech %B The Encyclopaedia of Physical Science and Technology, Volume 3 %E Robert A Meyers %I Academic Press %P 482-506 %K KInvited %A Witten, I.H. %A Bonham, M. %A Bramwell, B. %A Greenberg, S. %D in preparation %T Interacting with dynamic documents -- the new age of reading %R proposal submitted to MIT Press %A Witten, I.H. %D 1985 %T Selected topics in computer science I %R Report %I Institute of Information Processing, Graz, Austria %O November %K KReport %A Witten, I.H. %D 1986 %T Modeling behaviour sequences: principles, practice, prospects %J Proc International Conference on Future Advances in Computing %C Christchurch, New Zealand %O February 17-21 %K KConference %A Witten, I.H. %D 1986 %T In search of `autonomy' %J Proc International Conference on Future Advances in Computing %C Christchurch, New Zealand %O February 17-21 %K KConference %A Witten, I.H. %D 1986 %T Expert systems %J Man-Machine Studies %V UC-DSE %N 28 %P 5-65 %I University of Canterbury %C Christchurch, New Zealand %O May %K KArticle %A Witten, I.H. %D 1987 %T Thoughts on artificial intentionality %J Man-Machine Studies %V UC-DSE %N 9 %P 5-52 %I University of Canterbury %C Christchurch, New Zealand %O January %K KArticle %A Witten, I.H. %A Neal, R. %A Cleary, J.G. %D 1987 %T Arithmetic coding for data compression %J Comm ACM %V 30 %N 6 %P 520-540 %O June; reprinted in \fIC Gazette\fP, December 1987 %K KJournal %A Witten, I.H. %D 1987 %T A course on `expert systems' for electrical engineering students %J Proc ACM SIGCSE Technical Symposium on Computer Science Education %C St Louis, MO %P 257-260 %O February (published as SIGCSE Bulletin \fI19\fR(1)) %K KConference %A Wong, S.K.M %A Ziarko, W. %A Ye, R. Li %D 1985 %T Comparison of rough-set and statistical methods in inductive learning %R Technical Report CS-85-16 %I Computer Science Department, University of Regina %K * %A Wood, R.J. %D 1982 %T A window based display management system %R Internal Report %I University of Maryland %A Woods, W.A. %D 1973 %T Progress in natural language understanding -- an application to lunar geology %J Proc National Computer Conference %C Montvale, NJ %I AFIPS Press %A Woolf, B. %A McDonald, D.D. %D 1983 %T Human-computer discourse in the design of a Pascal tutor %J Proc ACM CHI 83 Human Factors in Computing Systems %P 230-234 %C Boston, MA %O December 12-15 %K * %A Woolf, B. %A McDonald, D.D. %D 1984 %T Building a computer tutor: design issues %J IEEE Computer %V 17 %N 9 %P 61-73 %O September %A Wright, W.E. %D 1977 %T Gravitational clustering %J Pattern Recognition %V 9 %P 151-166 %K * %A Wu, X. %A Witten, I.H. %D 1985 %T A fast \fIk-\fPmeans type clustering algorithm %R Research Report 85/197/10 %I Computer Science Department, University of Calgary %O June %A Wupit, A. %D 1983 %T Comparison of UNIX networks %J Proc ACM Conference on Personal and Small Computers %C San Diego, CA %P 99-108 %O December %A Wyvill, B.L.M. %D 1984 %T Three computer science plays: \fISquanderella\fP, \fIDigital Alice\fP, and \fITwenty eighty-four\fP %R Research Report %I Computer Science Department, University of Calgary %A Wyvill, B.L.M. %A Witten, I.H. %D 1984 %T Three computer science plays %R Research Report 84/184/42 %I Computer Science Department, University of Calgary %O December %K KReport %A Xerox\|Corp %D 1984 %T The role of electronic printing in the office of the future %R Executive Presentation II %K * %A Yankelovich, N. %A Meyrowitz, N. %A van Dam, A. %D 1985 %T Reading and writing the electronic book %J IEEE Computer %V 18 %N 10 %P 15-30 %O October %A Yoeli, M. %A Brzozowski, J.A. %D 1984 %T A mathematical model of digital CMOS networks %R Research Report CS-84-22 %I Computer Science Department, University of Waterloo %O August %K * %A Young, J.Z. %D 1978 %T Programs of the brain %I Oxford University Press %C Oxford, England %A Zaniolo, C. %D 1984 %T Object-oriented programming in Prolog %J Proc International Symposium on Logic Programming %C Atlantic City, NJ %P 265-270 %O February 6-9 %K * %A Zeleny, M. %D 1977 %T Self-organization of living systems: a formal model of autopoiesis %J Int J General Systems %V 4 %N 1 %P 13-28 %A Zeleny, M. %D 1978 %T Apl-autopoiesis: experiments in self-organization of complexity %B Progress in cybernetics and systems research III %E R.Trappl, G.J.Klir and L.Ricciardi %P 65-84 %I Hemisphere %C Washington, DC %A Zeleny, M.\0(Editor) %D 1981 %T Autopoiesis: a theory of living organization %I North Holland %C New York, NY %A Zeleny, M. %D 1981 %T What is autopoiesis? %E M.Zeleny %B Autopoiesis: a theory of living organization %I North Holland %C New York, NY %P 4-17 %A Zimmermann, H. %D 1980 %T OSI reference model \(em the ISO model of architecture for open systems interconnection %J IEEE Trans Communications %P 425-432 %O April %A Zisman, M.M. %D 1977 %T Representation, specification, and automation of office procedures %R PhD Dissertation %I Wharton School, University of Pennsylvania %A Zissos, A.Y. %A Witten, I.H. %D 1985 %T User modelling for a computer coach: a case study %J IJMMS %V 23 %N 6 %P 729-750 %O December %K KJournal %A Ziv, J. %A Lempel, A. %D 1977 %T A universal algorithm for sequential data compression %J IEEE Trans Information Theory %V IT-23 %N 3 %P 337-343 %O May %K * %A Ziv, J. %A Lempel, A. %D 1978 %T Compression of individual sequences via variable-rate coding %J IEEE Trans Information Theory %V IT-24 %P 530-536 %O September %K * \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/test4.bin b/loader/tools/exomizer-3.1/rawdecrs/test4.bin new file mode 100644 index 0000000..a35bd4a Binary files /dev/null and b/loader/tools/exomizer-3.1/rawdecrs/test4.bin differ diff --git a/loader/tools/exomizer-3.1/rawdecrs/thumb2/README.txt b/loader/tools/exomizer-3.1/rawdecrs/thumb2/README.txt new file mode 100644 index 0000000..a2a6477 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/thumb2/README.txt @@ -0,0 +1,56 @@ +This directory contains two exomizer decrunchers in assembly for 32bit +ARM with thumb2 instructions, contributed by ALeX Kazik (alex@kazik.de). +They are useful for embedded systems. + +The difference between the two files, universal.S and speed.S, is universal +supports all combinations of exomizer protocol flags as compile options, while +speed only supports a subset but is faster. + +They use only the stack and no global variables. + +Optionally two security checks can be enabled at compile flags: +- CHECK_BUFFER_SIZE - for input and output a buffer size has to be specified +- CHECK_OVERRUN - the input and output MUST be in the same buffer - check if the output overruns the input +(In case of a failure the output pointer is NULL.) + +To compile using gcc you can use the following command line: +> arm-none-eabi-gcc -c speed.S -mcpu=cortex-m4 + +(a ARM cpu with thumb2 instructions is required) + +The parameters are switched in contrast to the C version, this is +because the out pointer is passed on in r0 and never leaves - so the +result (r0) is the new out pointer. + +Here follows some benchmark numbers from an NUCLEO-64 board with a +STM32F411 on it. These numbers where measured decrunching test3 and was done +using 0 waitstates and without caching. +The speed and universal tests are, unless noted, without security checks, +identical to the C version. + +exodecr.c, -Os (P39): + 464 bytes, 8233408 cycles + +exodecr.c, -O3 (P39): +1716 bytes, 3800500 cycles + +exodecr.c, -Os (exomizer 3.0 = P7): + 468 bytes, 7993103 cycles + +exodecr.c, -O3 (exomizer 3.0 = P7): +1700 bytes, 3675802 cycles + +universal.S, P39: + 288 bytes, 2707475 cycles + +universal.S, P7: + 248 bytes, 2634823 cycles + +universal.S, P13: + 216 bytes, 2976219 cycles + +speed.S, P13: + 244 bytes, 1818600 cycles + +speed.S, P13, with both security checks enabled: + 284 bytes, 1941419 cycles diff --git a/loader/tools/exomizer-3.1/rawdecrs/thumb2/speed.S b/loader/tools/exomizer-3.1/rawdecrs/thumb2/speed.S new file mode 100644 index 0000000..7c77d38 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/thumb2/speed.S @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2020 by Alex Kazik + * + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +/* + * This decruncher is small and fast (at least than the generic one) + * The downside is that the bistream format is pretty fixed + * - BITS_ORDER_BE must be on + * - BITS_COPY_GT_7 must be off + * - IMPL_1LITERAL can be chosen + * - BITS_ALIGN_START must be set + * - 4_OFFSET_TABLES can be chosen + * - REUSE_OFFSET must be off + * + * the file has to be crunched with + * $ exomizer raw -C -b -P13 IN -o OUT + * you can change the P13 to anything valid, as described above: 9, 13, 25, 29 + * and as long as the value below is also changed + */ + +#define FLAGS_PROTO 13 + +/* + * there are two optional security options + * - CHECK_BUFFER_SIZE - for input and output a buffer size has to be specified + * - CHECK_OVERRUN - the input and output MUST be in the same buffer - check if the output overruns the input + * none, one or both can be used + * in case of a failure the output pointer is NULL + * to enable them just uncomment the following define + */ +//#define CHECK_BUFFER_SIZE +//#define CHECK_OVERRUN + +/* + * bit 0 Controls bit bit orientation, 1=big endian, 0=little endian + * bit 1 Contols how more than 7 bits are shifted 1=split into a shift of + * of less than 8 bits + a byte (new), 0=all bits are shifted + * bit 2 Implicit first literal byte: 1=enable, 0=disable + * bit 3 Align bit stream towards start without flag: 1=enable, 0=disable + * bit 4 Decides if we are to have two lengths (1 and 2) or three lengths + * (1, 2 and 3) using dedicated decrunch tables: 0=two, 1=three + * bit 5 Decides if we are reusing offsets: 1=enable, 0=disable + */ + +#define PBIT_BITS_ORDER_BE 0 +#define PBIT_BITS_COPY_GT_7 1 +#define PBIT_IMPL_1LITERAL 2 +#define PBIT_BITS_ALIGN_START 3 +#define PBIT_4_OFFSET_TABLES 4 +#define PBIT_REUSE_OFFSET 5 + +#define PFLAG_BITS_ORDER_BE (1 << PBIT_BITS_ORDER_BE) +#define PFLAG_BITS_COPY_GT_7 (1 << PBIT_BITS_COPY_GT_7) +#define PFLAG_IMPL_1LITERAL (1 << PBIT_IMPL_1LITERAL) +#define PFLAG_BITS_ALIGN_START (1 << PBIT_BITS_ALIGN_START) +#define PFLAG_4_OFFSET_TABLES (1 << PBIT_4_OFFSET_TABLES) +#define PFLAG_REUSE_OFFSET (1 << PBIT_REUSE_OFFSET) + +#if !defined(FLAGS_PROTO) || FLAGS_PROTO < 0 || FLAGS_PROTO > 63 +#error "FLAGS_PROTO must be set" +#endif + +#if !(FLAGS_PROTO & PFLAG_BITS_ORDER_BE) +#error "PFLAG_BITS_ORDER_BE is required" +#endif + +#if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 +#error "PFLAG_BITS_COPY_GT_7 is not allowed" +#endif + +#if !(FLAGS_PROTO & PFLAG_BITS_ALIGN_START) +#error "PFLAG_BITS_ALIGN_START is required" +#endif + +#if FLAGS_PROTO & PFLAG_REUSE_OFFSET +#error "PFLAG_REUSE_OFFSET is not allowed" +#endif + +.syntax unified +.thumb +.section .text +.global exo_decrunch +.type exo_decrunch,%function +.fnstart + + /* + PARAMETER: + r0 = output pointer + r1 = input pointer + r2 = output size (only with CHECK_BUFFER_SIZE) + r3 = input size (only with CHECK_BUFFER_SIZE) + + RETURN: + r0 = output pointer (null with a filed check) + r1 = input pointer + + GLOBAL REGISTER USAGE: + r0 = output pointer + r1 = input pointer + r2 = scratch / output get_bits + r3 = scratch / input get_bits + r4 = length (used different in init) + r5 = index / offset (used different in init) + r6 = length of bit_buffer + r7 = bit_buffer + r8 = bits pointer (52 / 68 bytes) + .. + r10 = output size (only with CHECK_BUFFER_SIZE) + r11 = input size (only with CHECK_BUFFER_SIZE) + r12 = scratch + .. + sp = base pointer (2*52 / 2*68 bytes) + */ + + #define reg_ptr_out r0 + #define reg_ptr_in r1 + #define reg_bit_buffer_length r6 + #define reg_bit_buffer r7 + #define reg_ptr_bits r8 + #define reg_out_size r10 + #define reg_in_size r11 + #define reg_ptr_base sp + +.p2align 2 +exo_decrunch: + #ifdef CHECK_BUFFER_SIZE + push {r4, r5, r6, r7, r8, r10, r11, lr} + #else + push {r4, r5, r6, r7, r8, lr} + #endif + + #ifdef CHECK_BUFFER_SIZE + // copy size + mov reg_out_size, r2 + mov reg_in_size, r3 + #endif + + // reserve stack + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + sub sp, sp, 2*52+52 + add reg_ptr_bits, sp, # 2*52 + #else + sub sp, sp, 2*68+68 + add reg_ptr_bits, sp, # 2*68 + #endif + + // init + movs reg_bit_buffer, # 0 + movs reg_bit_buffer_length, # 0 + + /* + init_table + r4 = "i" + r5 = "a" + r2 = "b" - when get_bits does not need to be called + */ + movs r4, # 0 +init_loop: + tst r4, # 0xf + iteee eq + moveq r5, # 1 + movne r3, # 1 + lslne r3, r3, r2 + addne r5, r5, r3 + strh r5, [reg_ptr_base, r4, LSL # 1] + + movs r3, # 4 + bl get_bits + strb r2, [reg_ptr_bits, r4] + adds r4, r4, # 1 + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + cmp r4, # 52 + #else + cmp r4, # 68 + #endif + bne init_loop + + /* + decrunch + r4 = length + r5 = index / offset + */ + #if FLAGS_PROTO & PFLAG_IMPL_1LITERAL + b literal_1_byte + #else + b main_loop + #endif + +get_offset: + // r5 = base index, r3 = bits to add to index + bl get_bits + adds.w r5, r2, r5 // update index + // fetch offset + ldrb r3, [reg_ptr_bits, r5] + bl get_bits + ldrh r5, [reg_ptr_base, r5, LSL # 1] + adds r5, r5, r2 + subs r5, r5, # 1 + #ifdef CHECK_BUFFER_SIZE + subs reg_out_size, r4 + bmi exit_failure + #endif + #ifdef CHECK_OVERRUN + sub r3, reg_ptr_out, r4 + cmp reg_ptr_in, r3 + bhs exit_failure + #endif +copy_loop: + ldrb r2, [reg_ptr_out, r5] + strb r2, [reg_ptr_out, #-1]! + subs r4, r4, # 1 + bne copy_loop + +main_loop: + movs r2, # 0 + // count how many zeroes are there before the first one +gamma_loop: + clz r3, reg_bit_buffer + cmp r3, reg_bit_buffer_length + bls gamma_end + // tried to read more than available, add all avilable bits and reload buffer + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + adds r2, r2, reg_bit_buffer_length + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsls reg_bit_buffer, reg_bit_buffer, # 24 + movs reg_bit_buffer_length, # 8 + b gamma_loop +gamma_end: + adds r2, r2, r3 + // update buffer: remove counted bits from it + adds r3, r3, # 1 + subs reg_bit_buffer_length, reg_bit_buffer_length, r3 + lsls reg_bit_buffer, reg_bit_buffer, r3 + + subs r2, r2, # 1 + bpl no_literal_1_byte + +literal_1_byte: + movs r2, # 1 + b copy_literal + +no_literal_1_byte: + cmp r2, # 16 + beq exit + blo no_literal_gamma + // index 17 -> copy literal sequence + movs r3, # 16 + bl get_bits + + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, r2 + bmi exit_failure + #endif + +copy_literal: + ldrb r3, [reg_ptr_in, #-1]! + strb r3, [reg_ptr_out, #-1]! + subs r2, r2, # 1 + bne copy_literal + #ifdef CHECK_BUFFER_SIZE + b main_loop + #else + b.w main_loop + #endif + +no_literal_gamma: + ldrh r4, [reg_ptr_base, r2, LSL # 1] + ldrb r3, [reg_ptr_bits, r2] + bl get_bits + adds r4, r4, r2 + + // copy length and saturate it + mov r2, r4 + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + usat r2, # 2, r2 + tbb [pc, r2] +tab: + .byte 0 // length zero never happens + .byte (len1 - tab) / 2 + .byte (len2 - tab) / 2 + .byte (len3ff - tab) / 2 +len1: + movs r3, # 2 + movs r5, # 48 + b get_offset +len2: + movs r3, # 4 + movs r5, # 32 + b get_offset +len3ff: + movs r3, # 4 + movs r5, # 16 + b get_offset + #else + usat r2, # 3, r2 + tbb [pc, r2] +tab: + .byte 0 // length zero never happens + .byte (len1 - tab) / 2 + .byte (len2 - tab) / 2 + .byte (len3 - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 +len1: + movs r3, # 2 + movs r5, # 64 + b get_offset +len2: + movs r3, # 4 + movs r5, # 48 + b get_offset +len3: + movs r3, # 4 + movs r5, # 32 + b get_offset +len4ff: + movs r3, # 4 + movs r5, # 16 + b get_offset + #endif + +#if defined(CHECK_BUFFER_SIZE) || defined(CHECK_OVERRUN) +exit_failure: + movs r0, # 0 +#endif + +exit: + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + add sp, sp, 2*52+52 + #else + add sp, sp, 2*68+68 + #endif + #ifdef CHECK_BUFFER_SIZE + pop {r4, r5, r6, r7, r8, r10, r11, pc} + #else + pop {r4, r5, r6, r7, r8, pc} + #endif + + /* + get_bits + input: r3 - number of bits to read + output: r2 - the read bits + scratch: r12 + */ + +.p2align 2 + +get_bits: + // check if there are enough bits in the buffer + cmp r3, reg_bit_buffer_length + bls read_it +refill: + // refill + rsb r12, reg_bit_buffer_length, # 24 + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb r2, [reg_ptr_in, #-1]! + lsls r2, r2, r12 + orrs reg_bit_buffer, reg_bit_buffer, r2 + add reg_bit_buffer_length, reg_bit_buffer_length, # 8 + // check again, and restart if still not ehough bits + cmp r3, reg_bit_buffer_length + bhi refill + + // read the specified amount of bits +read_it: + subs reg_bit_buffer_length, reg_bit_buffer_length, r3 + rsb r12, r3, # 32 + lsr r2, reg_bit_buffer, r12 + lsls reg_bit_buffer, reg_bit_buffer, r3 + + bx lr + +.fnend diff --git a/loader/tools/exomizer-3.1/rawdecrs/thumb2/universal.S b/loader/tools/exomizer-3.1/rawdecrs/thumb2/universal.S new file mode 100644 index 0000000..11b3c0f --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/thumb2/universal.S @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2020 by Alex Kazik + * + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +/* + * the file has to be crunched with + * $ exomizer raw -C -b -P39 IN -o OUT + * you can change the P39 to anything from 0-63 as long as the value below is also changed + */ + +#define FLAGS_PROTO 39 + +/* + * there are two optional security options + * - CHECK_BUFFER_SIZE - for input and output a buffer size has to be specified + * - CHECK_OVERRUN - the input and output MUST be in the same buffer - check if the output overruns the input + * none, one or both can be used + * in case of a failure the output pointer is NULL + * to enable them just uncomment the following define + */ +//#define CHECK_BUFFER_SIZE +//#define CHECK_OVERRUN + +/* + * bit 0 Controls bit bit orientation, 1=big endian, 0=little endian + * bit 1 Contols how more than 7 bits are shifted 1=split into a shift of + * of less than 8 bits + a byte (new), 0=all bits are shifted + * bit 2 Implicit first literal byte: 1=enable, 0=disable + * bit 3 Align bit stream towards start without flag: 1=enable, 0=disable + * bit 4 Decides if we are to have two lengths (1 and 2) or three lengths + * (1, 2 and 3) using dedicated decrunch tables: 0=two, 1=three + * bit 5 Decides if we are reusing offsets: 1=enable, 0=disable + */ + +#define PBIT_BITS_ORDER_BE 0 +#define PBIT_BITS_COPY_GT_7 1 +#define PBIT_IMPL_1LITERAL 2 +#define PBIT_BITS_ALIGN_START 3 +#define PBIT_4_OFFSET_TABLES 4 +#define PBIT_REUSE_OFFSET 5 + +#define PFLAG_BITS_ORDER_BE (1 << PBIT_BITS_ORDER_BE) +#define PFLAG_BITS_COPY_GT_7 (1 << PBIT_BITS_COPY_GT_7) +#define PFLAG_IMPL_1LITERAL (1 << PBIT_IMPL_1LITERAL) +#define PFLAG_BITS_ALIGN_START (1 << PBIT_BITS_ALIGN_START) +#define PFLAG_4_OFFSET_TABLES (1 << PBIT_4_OFFSET_TABLES) +#define PFLAG_REUSE_OFFSET (1 << PBIT_REUSE_OFFSET) + +#if !defined(FLAGS_PROTO) || FLAGS_PROTO < 0 || FLAGS_PROTO > 63 +#error "FLAGS_PROTO must be set" +#endif + +.syntax unified +.thumb +.section .text +.global exo_decrunch +.type exo_decrunch,%function +.fnstart + + /* + PARAMETER: + r0 = output pointer + r1 = input pointer + r2 = output size (only with CHECK_BUFFER_SIZE) + r3 = input size (only with CHECK_BUFFER_SIZE) + + RETURN: + r0 = output pointer (null with a filed check) + r1 = input pointer + + GLOBAL REGISTER USAGE: + r0 = output pointer + r1 = input pointer + r2 = scratch / output get_bits + r3 = scratch / input get_bits + r4 = length (used different in init) + r5 = index / offset (used different in init) + r6 = scratch get_bits (only when COPY_GT_7 is active) + r7 = bit_buffer + r8 = base pointer + .. + r10 = output size (only with CHECK_BUFFER_SIZE) + r11 = input size (only with CHECK_BUFFER_SIZE) + r12 = reuse_offset_state (only with PFLAG_REUSE_OFFSET) (used different in init) + .. + sp = base pointer (2*52 / 2*68 bytes) + */ + + #define reg_ptr_out r0 + #define reg_ptr_in r1 + #define reg_bit_buffer r7 + #define reg_ptr_bits r8 + #define reg_out_size r10 + #define reg_in_size r11 + #define reg_ptr_base sp + +.p2align 2 +exo_decrunch: + #ifdef CHECK_BUFFER_SIZE + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + push {r4, r5, r6, r7, r8, r10, r11, lr} + #else + push {r4, r5, r7, r8, r10, r11, lr} + #endif + #else + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + push {r4, r5, r6, r7, r8, lr} + #else + push {r4, r5, r7, r8, lr} + #endif + #endif + + #ifdef CHECK_BUFFER_SIZE + // copy size + mov reg_out_size, r2 + mov reg_in_size, r3 + #endif + + // reserve stack + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + sub sp, sp, 2*52+52 + add reg_ptr_bits, sp, # 2*52 + #else + sub sp, sp, 2*68+68 + add reg_ptr_bits, sp, # 2*68 + #endif + + // init + #if FLAGS_PROTO & PFLAG_BITS_ALIGN_START + movs reg_bit_buffer, # 0 + #else + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + #if FLAGS_PROTO & PFLAG_BITS_ORDER_BE + lsls reg_bit_buffer, reg_bit_buffer, # 24 + #endif + #endif + + /* + init_table + r4 = "i" + r5 = "a" + r2 = "b" - when get_bits does not need to be called + r12 = "b" - only temporary + */ + movs r4, # 0 +init_loop: + tst r4, # 0xf + iteee eq + moveq r5, # 1 + movne r3, # 1 + lslne r3, r3, r2 + addne r5, r5, r3 + strh r5, [reg_ptr_base, r4, LSL # 1] + + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + movs r3, # 3 + bl get_bits + mov r12, r2 + movs r3, # 1 + bl get_bits + lsls r2, r2, # 3 + orrs r2, r2, r12 + #else + movs r3, # 4 + bl get_bits + #endif + strb r2, [reg_ptr_bits, r4] + adds r4, r4, # 1 + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + cmp r4, # 52 + #else + cmp r4, # 68 + #endif + bne init_loop + + /* + decrunch + r4 = length + r5 = index / offset + r12 = reuse_offset_state (only with PFLAG_REUSE_OFFSET) + */ + #if FLAGS_PROTO & PFLAG_REUSE_OFFSET + mov r12, # 1 + #endif + #if FLAGS_PROTO & PFLAG_IMPL_1LITERAL + b literal_1_byte + #else + b main_loop + #endif + +get_offset: + // r5 = base index, r3 = bits to add to index + bl get_bits + adds r5, r2, r5 // correct index + // fetch offset + ldrb r3, [reg_ptr_bits, r5] + bl get_bits + ldrh r5, [reg_ptr_base, r5, LSL # 1] + adds r5, r5, r2 + subs r5, r5, # 1 + #ifdef CHECK_BUFFER_SIZE + subs reg_out_size, r4 + bmi exit_failure + #endif + #ifdef CHECK_OVERRUN + sub r3, reg_ptr_out, r4 + cmp reg_ptr_in, r3 + bhs exit_failure + #endif +copy_loop: + ldrb r2, [reg_ptr_out, r5] + strb r2, [reg_ptr_out, #-1]! + subs r4, r4, # 1 + bne copy_loop + + #if FLAGS_PROTO & PFLAG_REUSE_OFFSET + lsl r12, r12, # 16 + #endif + +main_loop: + movs r2, # 0 +gamma_loop: + adds r2, r2, 1 + + // get a single bit + #if FLAGS_PROTO & PFLAG_BITS_ORDER_BE + lsls reg_bit_buffer, reg_bit_buffer, # 1 + bne get_bit_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsls reg_bit_buffer, reg_bit_buffer, # 25 + orr reg_bit_buffer, reg_bit_buffer, # 1 << 24 + #else + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + bne get_bit_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + orr reg_bit_buffer, reg_bit_buffer, # 1 << 7 + #endif +get_bit_skip_reload: + + bcc gamma_loop + subs r2, r2, # 2 + bpl no_literal_1_byte + +literal_1_byte: + movs r2, # 1 + b copy_literal + +no_literal_1_byte: + cmp r2, # 16 + beq exit + blo no_literal_gamma + // index 17 -> copy literal sequence + + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 2 + bmi exit_failure + #endif + ldrh r2, [reg_ptr_in, #-2]! + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + rev16 r2, r2 + #endif + #else + movs r3, # 16 + bl get_bits + #endif + + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, r2 + bmi exit_failure + #endif + +copy_literal: + ldrb r3, [reg_ptr_in, #-1]! + strb r3, [reg_ptr_out, #-1]! + subs r2, r2, # 1 + bne copy_literal + #if FLAGS_PROTO & PFLAG_REUSE_OFFSET + lsl r12, r12, # 16 + orr r12, r12, # 1 + #endif + b main_loop + +no_literal_gamma: + ldrh r4, [reg_ptr_base, r2, LSL # 1] + ldrb r3, [reg_ptr_bits, r2] + bl get_bits + adds r4, r4, r2 + + // reuse offset? + #if FLAGS_PROTO & PFLAG_REUSE_OFFSET + cmp r12, # 1 + bne no_reuse + // get a bit + #if FLAGS_PROTO & PFLAG_BITS_ORDER_BE + lsls reg_bit_buffer, reg_bit_buffer, # 1 + bne reuse_get_bits_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsls reg_bit_buffer, reg_bit_buffer, # 25 + orr reg_bit_buffer, # 1 << 24 + #else + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + bne reuse_get_bits_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + orr reg_bit_buffer, # 1 << 7 + #endif +reuse_get_bits_skip_reload: + bcs copy_loop +no_reuse: + #endif + + // copy length and saturate it + mov r2, r4 + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + usat r2, # 2, r2 + tbb [pc, r2] +tab: + .byte 0 // length zero never happens + .byte (len1 - tab) / 2 + .byte (len2 - tab) / 2 + .byte (len3ff - tab) / 2 +len1: + movs r3, # 2 + movs r5, # 48 + b get_offset +len2: + movs r3, # 4 + movs r5, # 32 + b get_offset +len3ff: + movs r3, # 4 + movs r5, # 16 + b get_offset + #else + usat r2, # 3, r2 + tbb [pc, r2] +tab: + .byte 0 // length zero never happens + .byte (len1 - tab) / 2 + .byte (len2 - tab) / 2 + .byte (len3 - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 + .byte (len4ff - tab) / 2 +len1: + movs r3, # 2 + movs r5, # 64 + b get_offset +len2: + movs r3, # 4 + movs r5, # 48 + b get_offset +len3: + movs r3, # 4 + movs r5, # 32 + b get_offset +len4ff: + movs r3, # 4 + movs r5, # 16 + b get_offset + #endif + +#if defined(CHECK_BUFFER_SIZE) || defined(CHECK_OVERRUN) +exit_failure: + movs r0, # 0 +#endif + +exit: + #if !(FLAGS_PROTO & PFLAG_4_OFFSET_TABLES) + add sp, sp, 2*52+52 + #else + add sp, sp, 2*68+68 + #endif + #ifdef CHECK_BUFFER_SIZE + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + pop {r4, r5, r6, r7, r8, r10, r11, pc} + #else + pop {r4, r5, r7, r8, r10, r11, pc} + #endif + #else + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + pop {r4, r5, r6, r7, r8, pc} + #else + pop {r4, r5, r7, r8, pc} + #endif + #endif + + /* + get_bits + input: r3 - number of bits to read + output: r2 - the read bits + scratch: r6 (only when COPY_GT_7 is active) + */ + +.p2align 2 +get_bits: + movs r2, # 0 // output + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + lsr r6, r3, # 3 + and r3, r3, # 7 + #endif + cbz r3, get_bits_loop_end +get_bits_loop: + #if FLAGS_PROTO & PFLAG_BITS_ORDER_BE + lsls reg_bit_buffer, reg_bit_buffer, # 1 + bne get_bits_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsls reg_bit_buffer, reg_bit_buffer, # 25 + orr reg_bit_buffer, 1 << 24 + #else + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + bne get_bits_skip_reload + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb reg_bit_buffer, [reg_ptr_in, #-1]! + lsrs reg_bit_buffer, reg_bit_buffer, # 1 + orr reg_bit_buffer, # 1 << 7 + #endif +get_bits_skip_reload: + adcs r2, r2, r2 + + subs r3, r3, # 1 + bne get_bits_loop + +get_bits_loop_end: + #if FLAGS_PROTO & PFLAG_BITS_COPY_GT_7 + cbz r6, get_bits_byte_loop_end + lsls r2, r2, # 8 + #ifdef CHECK_BUFFER_SIZE + subs reg_in_size, # 1 + bmi exit_failure + #endif + ldrb r3, [reg_ptr_in, #-1]! + orrs r2, r2, r3 +get_bits_byte_loop_end: + #endif + + bx lr + +.fnend diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt.asm new file mode 100644 index 0000000..a31f6ef --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt.asm @@ -0,0 +1,463 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 [-b] (speed<3, literals=1) +; exomizer raw -P13 -T1 [-b] (speed<3, literals=0) +; exomizer raw -P15 -T0 [-b] (speed=3, literals=1) +; exomizer raw -P15 -T1 [-b] (speed=3, literals=0) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; +; SIZE speed 0 speed 1 speed 2 speed 3 range88-ef +; forw nolit 148 150 167 204 +2 +; back nolit 146 148 165 202 +2 +; forw liter 158 160 177 214 +3 +; back liter 156 158 175 212 +3 +; output deexoopt.bin +; define mapbase $5b00 +; define speed 3 +; define back 0 +; define literals 0 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +exinit ld c, 16 + jr nz, exget4 + ld de, 1 + ld ixl, c + IF speed=0 +exget4 call exgetb + ENDIF + IF speed=1 +exget4 add a, a + call z, exgetb + ENDIF + IF speed=2 OR speed=3 + defb 218 +exgb4 ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF +exget4 adc a, a + jr z, exgb4 + ENDIF + rl c + jr nc, exget4 + IF speed=0 OR speed=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 210 + ENDIF + IF speed=2 + inc c + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 48 + ENDIF + IF speed=3 + ex af, af' + ld a, c + rrca + inc a + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), a + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), a + ENDIF + jr nc, get5 + xor 136 +get5 push hl + ld hl, 1 + defb 56 +setbit add hl, hl + dec a + jr nz, setbit + ex af, af' + ELSE +exsetb add hl, hl + dec c + jr nz, exsetb + ENDIF + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz exinit + pop de + + IF back=1 + IF literals=1 AND speed=0 +exlit inc c +exseq lddr + ELSE +exlit ldd + ENDIF + ELSE + IF literals=1 AND speed=0 +exlit inc c +exseq ldir + ELSE +exlit ldi + ENDIF + ENDIF + IF speed=0 +exloop call exgetb + jr c, exlit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +exgeti call exgetb + ENDIF + IF speed=1 +exloop add a, a + call z, exgetb + jr c, exlit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +exgeti add a, a + call z, exgetb + ENDIF + IF speed=2 OR speed=3 +exloop add a, a + jr z, exgbm + jr c, exlit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +exgbmc ld c, 256-1 + ELSE +exgbmc ld c, 112-1 + ENDIF +exgeti add a, a + jr z, exgbi +exgbic inc c + jr nc, exgeti + ccf + ELSE +exgbic inc c + jr nc, exgeti + ENDIF + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, excat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, excat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + IF speed=2 OR speed=3 + ld de, 0 + ENDIF + IF speed=3 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, exgbts + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ELSE + call expair + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, exgoit + dec e + ld bc, 1024+32 + jr z, exgoit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, exgoit + dec e + ld bc, 1024+144 + jr z, exgoit + ld c, 128 + ENDIF + IF speed=0 OR speed=1 +exgoit call exgbts + ENDIF + IF speed=2 + ld e, 0 +exgoit ld d, e + call exgbts + ENDIF + IF speed=3 + ld e, 0 +exgoit ld d, e + call exlee8 + ENDIF + ld iyl, c + add iy, de + IF speed=2 OR speed=3 + ld e, d + ENDIF + IF speed=3 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, exgbts + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ELSE + call expair + ENDIF + pop bc + ex (sp), hl + IF back=1 + ex de, hl + add hl, de + lddr + ELSE + push hl + sbc hl, de + pop de + ldir + ENDIF + pop hl + jr exloop + + IF literals=1 +excat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + IF speed=3 + ld b, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + ld c, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + ELSE + push de + IF speed=2 + ld de, 0 + ENDIF + ld b, 16 + call exgbts + ld b, d + ld c, e + pop de + ENDIF + IF speed=0 + jr exseq + ELSE + IF back=1 + lddr + ELSE + ldir + ENDIF + jr exloop + ENDIF + ENDIF + + IF speed=2 OR speed=3 +exgbm ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + adc a, a + jr nc, exgbmc + jp exlit +exgbi ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + adc a, a + jp exgbic + ENDIF + IF speed=3 +exgbts jp p, exlee8 + rl b + jr z, exgby + srl b + defb 250 +exxopy ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF +exl16 adc a, a + jr z, exxopy + rl d + djnz exl16 +exgby ld e, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + ret +excopy ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF +exlee8 adc a, a + jr z, excopy + rl e + djnz exlee8 + ret + ELSE + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +expair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +expair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + IF speed=2 + dec b + call nz, exgbts + ELSE + call exgbts + ENDIF + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + ENDIF + IF speed=0 OR speed=1 +exgbts ld de, 0 +excont dec b + ret m + IF speed=0 + call exgetb + ELSE + add a, a + call z, exgetb + ENDIF + rl e + rl d + jr excont + IF speed=0 +exgetb add a, a + ret nz + ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + adc a, a + ret + ELSE +exgetb ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF + adc a, a + ret + ENDIF + ENDIF + IF speed=2 +exgbg ld a, (hl) + IF back=1 + dec hl + ELSE + inc hl + ENDIF +exgbts adc a, a + jr z, exgbg + rl e + rl d + djnz exgbts + ret + ENDIF \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b0.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b0.asm new file mode 100644 index 0000000..07d497f --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b0.asm @@ -0,0 +1,229 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 -b (literals=1) (reuse=0) +; exomizer raw -P13 -T1 -b (literals=0) (reuse=0) +; exomizer raw -P45 -T0 -b (literals=1) (reuse=1) +; exomizer raw -P45 -T1 -b (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c +get4 call getbit + rl c + jr nc, get4 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 210 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de + IF literals=1 +litcop inc c +litseq lddr + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ELSE +litcop ldd + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ENDIF +mloop call getbit + jr c, litcop + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind call getbit + inc c + jr nc, getind + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF +goit + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + call getbit + ld de, (mapbase+156) + jr c, caof +aqui + ENDIF + call getbits + ld iyl, c + add iy, de + call getpair + IF reuse=1 + ld (mapbase+156), de + ENDIF +caof pop bc + ex (sp), hl + ex de, hl + add hl, de + lddr + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + IF reuse=1 + scf + ENDIF + jr litseq + ENDIF + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + call getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +getbits ld de, 0 +gbcont dec b + ret m + call getbit + rl e + rl d + jr gbcont + +getbit add a, a + ret nz + ld a, (hl) + dec hl + adc a, a + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b1.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b1.asm new file mode 100644 index 0000000..2c6783a --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b1.asm @@ -0,0 +1,222 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 -b (literals=1) (reuse=0) +; exomizer raw -P13 -T1 -b (literals=0) (reuse=0) +; exomizer raw -P45 -T0 -b (literals=1) (reuse=1) +; exomizer raw -P45 -T1 -b (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c +get4 add a, a + call z, getbit + rl c + jr nc, get4 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 210 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de +litcop ldd +mloo1 + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF +mloop add a, a + call z, getbit + jr c, litcop + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + call z, getbit + inc c + jr nc, getind + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF +goit + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + call z, getbit + ld de, (mapbase+156) + jr c, caof +aqui + ENDIF + call getbits + ld iyl, c + add iy, de + call getpair + IF reuse=1 + ld (mapbase+156), de + ENDIF +caof pop bc + ex (sp), hl + ex de, hl + add hl, de + lddr + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + lddr + IF reuse=1 + scf + ENDIF + jr mloo1 + ENDIF + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + call getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +getbits ld de, 0 +gbcont dec b + ret m + add a, a + call z, getbit + rl e + rl d + jr gbcont + +getbit ld a, (hl) + dec hl + adc a, a + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b2.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b2.asm new file mode 100644 index 0000000..8e4462e --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b2.asm @@ -0,0 +1,253 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 -b (literals=1) (reuse=0) +; exomizer raw -P13 -T1 -b (literals=0) (reuse=0) +; exomizer raw -P45 -T0 -b (literals=1) (reuse=1) +; exomizer raw -P45 -T1 -b (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + dec hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + inc c + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 48 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de +litcop ldd +mloo1 + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF +mloop add a, a + jr z, gbm + jr c, litcop +gbmc + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + jr z, gbi +gbic inc c + jr nc, getind + ccf + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + ld de, 0 + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + and a + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF + IF reuse=1 +goit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + ld de, (mapbase+156) + jr z, gba + jr c, caof +aqui ld de, 0 + ELSE + ld e, 0 +goit ld d, e + ENDIF + call getbits + ld iyl, c + add iy, de + ld e, d + call getpair + IF reuse=1 + ld (mapbase+156), de +caof and a + ELSE +caof + ENDIF + pop bc + ex (sp), hl + ex de, hl + add hl, de + lddr + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld de, 0 + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + IF reuse=1 + scf + ENDIF + lddr + jr mloo1 + ENDIF + + IF reuse=1 +gba ld a, (hl) + dec hl + adc a, a + jr nc, aqui + jp caof + ENDIF + +gbm ld a, (hl) + dec hl + adc a, a + jr nc, gbmc + jp litcop + +gbi ld a, (hl) + dec hl + adc a, a + jp gbic + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +gbg ld a, (hl) + dec hl +getbits adc a, a + jr z, gbg + rl e + rl d + djnz getbits + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b3.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b3.asm new file mode 100644 index 0000000..50db822 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b3.asm @@ -0,0 +1,290 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P15 -T0 -b (literals=1) (reuse=0) +; exomizer raw -P15 -T1 -b (literals=0) (reuse=0) +; exomizer raw -P47 -T0 -b (literals=1) (reuse=1) +; exomizer raw -P47 -T1 -b (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + dec hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + ex af, af' + ld a, c + rrca + inc a + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), a + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), a + ENDIF + jr nc, get5 + xor 136 +get5 push hl + ld hl, 1 + defb 56 +setbit add hl, hl + dec a + jr nz, setbit + ex af, af' + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de +litcop ldd +mloo1 + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF +mloop add a, a + IF reuse=0 + jr z, gbm + ELSE + jr nz, gbm + ld a, (hl) + dec hl + adc a, a +gbm + ENDIF + jr c, litcop +gbmc + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + jr z, gbi +gbic inc c + jr nc, getind + ccf + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + ld de, 0 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + and a + ENDIF + ex de, hl + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF + IF reuse=1 +goit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + ld de, (mapbase+156) + jr z, gba + jr c, caof +aqui ld de, 0 + ELSE + ld e, 0 +goit ld d, e + ENDIF + call lee8 + ld iyl, c + add iy, de + ld e, d + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + IF reuse=1 + ld (mapbase+156), hl + ENDIF + ex de, hl +caof pop bc + ex (sp), hl + ex de, hl + add hl, de + lddr + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + ld b, (hl) + dec hl + ld c, (hl) + dec hl + lddr + IF reuse=0 + jr mloo1 + ELSE + scf + jp mloo1 + ENDIF + ENDIF + +gbi ld a, (hl) + dec hl + adc a, a + jp gbic + + IF reuse=0 +gbm ld a, (hl) + dec hl + adc a, a + jr nc, gbmc + jp litcop + ELSE +gba ld a, (hl) + dec hl + adc a, a + jr nc, aqui + jp caof + ENDIF + +getbits jp p, lee8 + rl b + jr z, gby + srl b + defb 250 +xopy ld a, (hl) + dec hl +lee16 adc a, a + jr z, xopy + rl d + djnz lee16 +gby ld e, (hl) + dec hl + ret + +copy ld a, (hl) + dec hl +lee8 adc a, a + jr z, copy + rl e + djnz lee8 + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b4.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b4.asm new file mode 100644 index 0000000..283a188 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_b4.asm @@ -0,0 +1,178 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P15 -T1 +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; +; mapbase low byte must $00 + + xor a + ld a, 128 + ld iyl, 240 + ld b, 52 + push de +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + dec hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + ld iyh, mapbase/256 + ex af, af' + ld a, c + rrca + inc a + ld (iy+0), a + jr nc, get5 + xor 136 +get5 push hl + ld hl, 1 + defb 56 +setbit add hl, hl + dec a + jr nz, setbit + ex af, af' + inc iyh + ld (iy+0), e + inc iyh + ld (iy+0), d + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + pop de +litcop ldd +mloop add a, a + jr z, gbm + jr c, litcop +gbmc ld bc, mapbase+240 + add a, a + jr z, gbi2 + jr c, gbic +getind add a, a + jr z, gbi +geti2 inc c + jr nc, getind + ret z +gbic and a + push de + ex af, af' + ld a, (bc) + ld b, a + ex af, af' + ld de, 0 + dec b + call nz, getbits + ex af, af' + ld b, (mapbase/256)+1 + ld a, (bc) + inc b + add a, e + ld e, a + ld a, (bc) + adc a, d + ld d, a + push de + ld bc, 512+32 + dec e + jr z, goit + dec e + ld bc, 1024+16 + jr z, goit + ld c, 0 + ld e, c +goit ld d, e + ex af, af' + call lee8 + ex af, af' + ld a, e + add a, c + ld c, a + ld e, d + ld b, mapbase/256 + ld a, (bc) + ld b, a + ex af, af' + dec b + call nz, getbits + ex af, af' + ld b, (mapbase/256)+1 + ld a, (bc) + inc b + add a, e + ld e, a + ld a, (bc) + adc a, d + ld d, a + ex af, af' + pop bc + ex (sp), hl + ex de, hl + add hl, de + lddr + pop hl + jp mloop + +gbi ld a, (hl) + dec hl + adc a, a + jr nc, geti2 + inc c + jp nz, gbic + ret +gbi2 ld a, (hl) + dec hl + adc a, a + jr nc, getind + jp gbic +gbm ld a, (hl) + dec hl + adc a, a + jr nc, gbmc + jp litcop + +getbits jp p, lee8 + rl b + jr z, gby + srl b + defb $fa +xopy ld a, (hl) + dec hl +lee16 adc a, a + jr z, xopy + rl d + djnz lee16 +gby ld e, (hl) + dec hl + ret + +copy ld a, (hl) + dec hl +lee8 adc a, a + jr z, copy + rl e + djnz lee8 + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f0.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f0.asm new file mode 100644 index 0000000..fe6c70f --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f0.asm @@ -0,0 +1,231 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 (literals=1) (reuse=0) +; exomizer raw -P13 -T1 (literals=0) (reuse=0) +; exomizer raw -P45 -T0 (literals=1) (reuse=1) +; exomizer raw -P45 -T1 (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c +get4 call getbit + rl c + jr nc, get4 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 210 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de + IF literals=1 +litcop inc c +litseq ldir + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ELSE +litcop ldi + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ENDIF +mloop call getbit + jr c, litcop + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind call getbit + inc c + jr nc, getind + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF +goit + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + call getbit + ld de, (mapbase+156) + ccf + jr nc, caof +aqui + ENDIF + call getbits + ld iyl, c + add iy, de + call getpair + IF reuse=1 + ld (mapbase+156), de + ENDIF +caof pop bc + ex (sp), hl + push hl + sbc hl, de + pop de + ldir + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + IF reuse=1 + scf + ENDIF + jr litseq + ENDIF + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + call getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +getbits ld de, 0 +gbcont dec b + ret m + call getbit + rl e + rl d + jr gbcont + +getbit add a, a + ret nz + ld a, (hl) + inc hl + adc a, a + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f1.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f1.asm new file mode 100644 index 0000000..bba10ae --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f1.asm @@ -0,0 +1,234 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 (literals=1) (reuse=0) +; exomizer raw -P13 -T1 (literals=0) (reuse=0) +; exomizer raw -P45 -T0 (literals=1) (reuse=1) +; exomizer raw -P45 -T1 (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c +get4 add a, a + call z, getbit + rl c + jr nc, get4 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 210 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de + IF literals=1 +litcop inc c +litseq ldir + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ELSE +litcop ldi + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + ENDIF +mloop add a, a + call z, getbit + jr c, litcop + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + call z, getbit + inc c + jr nc, getind + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF +goit + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + call z, getbit + ld de, (mapbase+156) + ccf + jr nc, caof +aqui + ENDIF + call getbits + ld iyl, c + add iy, de + call getpair + IF reuse=1 + ld (mapbase+156), de + ENDIF +caof pop bc + ex (sp), hl + push hl + sbc hl, de + pop de + ldir + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + IF reuse=1 + scf + ENDIF + jr litseq + ENDIF + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + call getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +getbits ld de, 0 +gbcont dec b + ret m + add a, a + call z, getbit + rl e + rl d + jr gbcont + +getbit ld a, (hl) + inc hl + adc a, a + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f2.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f2.asm new file mode 100644 index 0000000..003390f --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f2.asm @@ -0,0 +1,254 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P13 -T0 (literals=1) (reuse=0) +; exomizer raw -P13 -T1 (literals=0) (reuse=0) +; exomizer raw -P45 -T0 (literals=1) (reuse=1) +; exomizer raw -P45 -T1 (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + inc hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + inc c + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), c + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), c + ENDIF + push hl + ld hl, 1 + defb 48 +setbit add hl, hl + dec c + jr nz, setbit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de +litcop ldi +mloo1 + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF +mloop add a, a + jr z, gbm + jr c, litcop +gbmc + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + jr z, gbi +gbic inc c + jr nc, getind + ccf + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + ld de, 0 + call getpair + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + and a + ENDIF + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF + IF reuse=1 +goit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + ld de, (mapbase+156) + jr z, gba + jr c, caof +aqui ld de, 0 + ELSE + ld e, 0 +goit ld d, e + ENDIF + call getbits + ld iyl, c + add iy, de + ld e, d + call getpair + IF reuse=1 + ld (mapbase+156), de +caof and a + ELSE +caof + ENDIF + pop bc + ex (sp), hl + push hl + sbc hl, de + pop de + ldir + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + push de + ld de, 0 + ld b, 16 + call getbits + ld b, d + ld c, e + pop de + IF reuse=1 + scf + ENDIF + ldir + jr mloo1 + ENDIF + + IF reuse=1 +gba ld a, (hl) + inc hl + adc a, a + jr nc, aqui + jp caof + ENDIF + +gbm ld a, (hl) + inc hl + adc a, a + jr nc, gbmc + jp litcop + +gbi ld a, (hl) + inc hl + adc a, a + jp gbic + + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 +getpair ld b, (iy-256+mapbase-mapbase/256*256) + ELSE +getpair ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + ex de, hl + ret + +gbg ld a, (hl) + inc hl +getbits adc a, a + jr z, gbg + rl e + rl d + djnz getbits + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f3.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f3.asm new file mode 100644 index 0000000..0515a1c --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f3.asm @@ -0,0 +1,295 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P15 -T0 (literals=1) (reuse=0) +; exomizer raw -P15 -T1 (literals=0) (reuse=0) +; exomizer raw -P47 -T0 (literals=1) (reuse=1) +; exomizer raw -P47 -T1 (literals=0) (reuse=1) +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld iy, 256+mapbase/256*256 + ELSE + ld iy, (mapbase+16)/256*256+112 + ENDIF + ld a, 128 + ld b, 52 + push de + cp a +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + inc hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + ex af, af' + ld a, c + rrca + inc a + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-256+mapbase-mapbase/256*256), a + ELSE + ld (iy-112+mapbase-(mapbase+16)/256*256), a + ENDIF + jr nc, get5 + xor 136 +get5 push hl + ld hl, 1 + defb 56 +setbit add hl, hl + dec a + jr nz, setbit + ex af, af' + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (iy-204+mapbase-mapbase/256*256), e + ld (iy-152+mapbase-mapbase/256*256), d + ELSE + ld (iy-60+mapbase-(mapbase+16)/256*256), e + ld (iy-8+mapbase-(mapbase+16)/256*256), d + ENDIF + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + IF reuse=1 + push iy + pop ix + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld (ix-152+mapbase-mapbase/256*256), 1 + ELSE + ld (ix-6+mapbase-(mapbase+16)/256*256), 1 + ENDIF + scf + ENDIF + pop de +litcop ldi +mloo1 + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + ENDIF +mloop add a, a + IF reuse=0 + jr z, gbm + ELSE + jr nz, gbm + ld a, (hl) + inc hl + adc a, a +gbm + ENDIF + jr c, litcop +gbmc + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, 256-1 + ELSE + ld c, 112-1 + ENDIF +getind add a, a + jr z, gbi +gbic inc c + jr nc, getind + ccf + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + bit 4, c + IF literals=1 + jr nz, litcat + ELSE + ret nz + ENDIF + ELSE + IF literals=1 + jp m, litcat + ELSE + ret m + ENDIF + ENDIF + push de + ld iyl, c + ld de, 0 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + IF reuse=1 + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl (ix-152+mapbase-mapbase/256*256) + ELSE + rl (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + and a + ENDIF + ex de, hl + push de + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld bc, 512+48 + dec e + jr z, goit + dec e + ld bc, 1024+32 + jr z, goit + ld c, 16 + ELSE + ld bc, 512+160 + dec e + jr z, goit + dec e + ld bc, 1024+144 + jr z, goit + ld c, 128 + ENDIF + IF reuse=1 +goit + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld e, (ix-152+mapbase-mapbase/256*256) + ELSE + ld e, (ix-6+mapbase-(mapbase+16)/256*256) + ENDIF + bit 1, e + jr z, aqui + bit 2, e + jr nz, aqui + add a, a + ld de, (mapbase+156) + jr z, gba + jr c, caof +aqui ld de, 0 + ELSE + ld e, 0 +goit ld d, e + ENDIF + call lee8 + ld iyl, c + add iy, de + ld e, d + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld b, (iy-256+mapbase-mapbase/256*256) + ELSE + ld b, (iy-112+mapbase-(mapbase+16)/256*256) + ENDIF + dec b + call nz, getbits + ex de, hl + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + ld c, (iy-204+mapbase-mapbase/256*256) + ld b, (iy-152+mapbase-mapbase/256*256) + ELSE + ld c, (iy-60+mapbase-(mapbase+16)/256*256) + ld b, (iy-8+mapbase-(mapbase+16)/256*256) + ENDIF + add hl, bc + IF reuse=0 + ex de, hl +caof + ELSE + ld (mapbase+156), hl + ex de, hl +caof and a + ENDIF + pop bc + ex (sp), hl + push hl + sbc hl, de + pop de + ldir + pop hl + jr mloop + + IF literals=1 +litcat + IF mapbase-mapbase/256*256<240 AND mapbase-mapbase/256*256>135 + rl c + ENDIF + ret pe + ld b, (hl) + inc hl + ld c, (hl) + inc hl + ldir + IF reuse=0 + jr mloo1 + ELSE + scf + jp mloo1 + ENDIF + ENDIF + +gbi ld a, (hl) + inc hl + adc a, a + jp gbic + + IF reuse=0 +gbm ld a, (hl) + inc hl + adc a, a + jr nc, gbmc + jp litcop + ELSE +gba ld a, (hl) + inc hl + adc a, a + jr nc, aqui + jp caof + ENDIF + +getbits jp p, lee8 + rl b + jr z, gby + srl b + defb 250 +xopy ld a, (hl) + inc hl +lee16 adc a, a + jr z, xopy + rl d + djnz lee16 +gby ld e, (hl) + inc hl + ret + +copy ld a, (hl) + inc hl +lee8 adc a, a + jr z, copy + rl e + djnz lee8 + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f4.asm b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f4.asm new file mode 100644 index 0000000..17cb67c --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/deexoopt_f4.asm @@ -0,0 +1,181 @@ +;Exomizer 3 Z80 decoder +;Copyright (C) 2008-2018 by Jaime Tejedor Gomez (Metalbrain) +; +;Optimized by Antonio Villena and Urusergi +; +;Compression algorithm by Magnus Lind +; exomizer raw -P15 -T1 +; +; This depacker is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +; +; mapbase low byte must $00 + + xor a + ld a, 128 + ld iyl, 240 + ld b, 52 + push de +init ld c, 16 + jr nz, get4 + ld de, 1 + ld ixl, c + defb 218 +gb4 ld a, (hl) + inc hl +get4 adc a, a + jr z, gb4 + rl c + jr nc, get4 + ld iyh, mapbase/256 + ex af, af' + ld a, c + rrca + inc a + ld (iy+0), a + jr nc, get5 + xor 136 +get5 push hl + ld hl, 1 + defb 56 +setbit add hl, hl + dec a + jr nz, setbit + ex af, af' + inc iyh + ld (iy+0), e + inc iyh + ld (iy+0), d + add hl, de + ex de, hl + inc iyl + pop hl + dec ixl + djnz init + pop de +litcop ldi +mloop add a, a + jr z, gbm + jr c, litcop +gbmc ld bc, mapbase+240 + add a, a + jr z, gbi2 + jr c, gbic +getind add a, a + jr z, gbi +geti2 inc c + jr nc, getind + ret z +gbic and a + push de + ex af, af' + ld a, (bc) + ld b, a + ex af, af' + ld de, 0 + dec b + call nz, getbits + ex af, af' + ld b, (mapbase/256)+1 + ld a, (bc) + inc b + add a, e + ld e, a + ld a, (bc) + adc a, d + ld d, a + push de + ld bc, 512+32 + dec e + jr z, goit + dec e + ld bc, 1024+16 + jr z, goit + ld c, 0 + ld e, c +goit ld d, e + ex af, af' + call lee8 + ex af, af' + ld a, e + add a, c + ld c, a + ld e, d + ld b, mapbase/256 + ld a, (bc) + ld b, a + ex af, af' + dec b + call nz, getbits + ex af, af' + ld b, (mapbase/256)+1 + ld a, (bc) + inc b + add a, e + ld e, a + ld a, (bc) + adc a, d + ld d, a + ex af, af' + pop bc + ex (sp), hl + push hl + sbc hl, de + pop de + ldir + pop hl + jp mloop + + +gbi ld a, (hl) + inc hl + adc a, a + jr nc, geti2 + inc c + jp nz, gbic + ret +gbi2 ld a, (hl) + inc hl + adc a, a + jr nc, getind + jp gbic +gbm ld a, (hl) + inc hl + adc a, a + jr nc, gbmc + jp litcop + +getbits jp p, lee8 + res 7, b + dec b + jp m, gby + inc b + defb $fa +xopy ld a, (hl) + inc hl +lee16 adc a, a + jr z, xopy + rl d + djnz lee16 +gby ld e, (hl) + inc hl + ret + +copy ld a, (hl) + inc hl +lee8 adc a, a + jr z, copy + rl e + djnz lee8 + ret \ No newline at end of file diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/lgpl-2.1.txt b/loader/tools/exomizer-3.1/rawdecrs/z80/lgpl-2.1.txt new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/lgpl-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/loader/tools/exomizer-3.1/rawdecrs/z80/readme.txt b/loader/tools/exomizer-3.1/rawdecrs/z80/readme.txt new file mode 100644 index 0000000..dff5df0 --- /dev/null +++ b/loader/tools/exomizer-3.1/rawdecrs/z80/readme.txt @@ -0,0 +1,17 @@ +Decompressor routines for Exomizer 2 & 3 by Magnus Lind + +https://bitbucket.org/magli143/exomizer + +Exomizer 2 routines are deexo****.asm files (4 variants): +-simple: no literal sequences and exo_mapbasebits aligned to a 256 boundary. +-b: backwards. + +Exomizer 3 routines are deexoopt_XY.asm (10 variants): +-b for backwards, f for forwards. +-0..4 is the speed of the algorithm. 4 is without literal sequences and +mapbase aligned to a 256 boundary. +-deexoopt.asm is the unification of 8 variants (speed 4 not included). + +The original decompressor was written by Jaime Tejedor Gomez (Metalbrain). +The assembler used is SjAsmPlus. For use with others assemblers maybe you +must adapt the conditional assembly directives. diff --git a/loader/tools/exomizer-3.1/src/6502emu.c b/loader/tools/exomizer-3.1/src/6502emu.c new file mode 100644 index 0000000..f84bc6c --- /dev/null +++ b/loader/tools/exomizer-3.1/src/6502emu.c @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 2007 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "6502emu.h" +#include "log.h" +#include + +#define FLAG_N 128 +#define FLAG_V 64 +#define FLAG_D 8 +#define FLAG_I 4 +#define FLAG_Z 2 +#define FLAG_C 1 + +struct arg_ea +{ + u16 value; +}; +struct arg_relative +{ + i8 value; +}; +struct arg_immediate +{ + u8 value; +}; + +union inst_arg +{ + struct arg_ea ea; + struct arg_relative rel; + struct arg_immediate imm; +}; + +typedef void op_f(struct cpu_ctx *r, int mode, union inst_arg *arg); +typedef int mode_f(struct cpu_ctx *r, union inst_arg *arg); + +struct op_info +{ + op_f *f; + char *fmt; +}; + +struct mode_info +{ + mode_f *f; + char *fmt; +}; + +struct inst_info +{ + struct op_info *op; + struct mode_info *mode; + u8 cycles; +}; + +u16 mem_access_read_u16le(struct mem_access *this, u16 address) +{ + u16 value; + value = MEM_ACCESS_READ(this, address); + value |= MEM_ACCESS_READ(this, address + 1) << 8; + return value; +} + +#define MODE_IMMEDIATE 0 +#define MODE_ZERO_PAGE 1 +#define MODE_ZERO_PAGE_X 2 +#define MODE_ZERO_PAGE_Y 3 +#define MODE_ABSOLUTE 4 +#define MODE_ABSOLUTE_X 5 +#define MODE_ABSOLUTE_Y 6 +#define MODE_INDIRECT 7 +#define MODE_INDIRECT_X 8 +#define MODE_INDIRECT_Y 9 +#define MODE_RELATIVE 10 +#define MODE_ACCUMULATOR 11 +#define MODE_IMPLIED 12 + +static int mode_imm(struct cpu_ctx *r, union inst_arg *arg) +{ + arg->imm.value = MEM_ACCESS_READ(&r->mem, r->pc + 1); + r->pc += 2; + return MODE_IMMEDIATE; +} +static int mode_zp(struct cpu_ctx *r, union inst_arg *arg) +{ + arg->ea.value = MEM_ACCESS_READ(&r->mem, r->pc + 1); + r->pc += 2; + return MODE_ZERO_PAGE; +} +static int mode_zpx(struct cpu_ctx *r, union inst_arg *arg) +{ + u8 lsbLo = MEM_ACCESS_READ(&r->mem, r->pc + 1) + r->x; + arg->ea.value = lsbLo; + r->pc += 2; + return MODE_ZERO_PAGE_X; +} +static int mode_zpy(struct cpu_ctx *r, union inst_arg *arg) +{ + u8 lsbLo = MEM_ACCESS_READ(&r->mem, r->pc + 1) + r->y; + arg->ea.value = lsbLo; + r->pc += 2; + return MODE_ZERO_PAGE_Y; +} +static int mode_abs(struct cpu_ctx *r, union inst_arg *arg) +{ + u16 offset = MEM_ACCESS_READ(&r->mem, r->pc + 1); + u16 base = MEM_ACCESS_READ(&r->mem, r->pc + 2) << 8; + arg->ea.value = base + offset; + r->pc += 3; + return MODE_ABSOLUTE; +} +static int mode_absx(struct cpu_ctx *r, union inst_arg *arg) +{ + u16 offset = MEM_ACCESS_READ(&r->mem, r->pc + 1) + r->x; + u16 base = MEM_ACCESS_READ(&r->mem, r->pc + 2) << 8; + arg->ea.value = base + offset; + r->pc += 3; + r->cycles += (offset > 255); + return MODE_ABSOLUTE_X; +} +static int mode_absy(struct cpu_ctx *r, union inst_arg *arg) +{ + u16 offset = MEM_ACCESS_READ(&r->mem, r->pc + 1) + r->y; + u16 base = MEM_ACCESS_READ(&r->mem, r->pc + 2) << 8; + arg->ea.value = base + offset; + r->pc += 3; + r->cycles += (offset > 255); + return MODE_ABSOLUTE_Y; +} +static int mode_ind(struct cpu_ctx *r, union inst_arg *arg) +{ + u8 lsbLo = MEM_ACCESS_READ(&r->mem, r->pc + 1); + u8 lsbHi = MEM_ACCESS_READ(&r->mem, r->pc + 2); + u8 msbLo = lsbLo + 1; + u8 msbHi = lsbHi; + u16 base = MEM_ACCESS_READ(&r->mem, msbLo + (msbHi << 8)) << 8; + u16 offset = MEM_ACCESS_READ(&r->mem, lsbLo + (lsbHi << 8)); + arg->ea.value = base + offset; + r->pc += 3; + return MODE_INDIRECT; +} +static int mode_indx(struct cpu_ctx *r, union inst_arg *arg) +{ + u8 lsbLo = MEM_ACCESS_READ(&r->mem, r->pc + 1) + r->x; + u8 msbLo = lsbLo + 1; + u16 base = MEM_ACCESS_READ(&r->mem, msbLo) << 8; + u16 offset = MEM_ACCESS_READ(&r->mem, lsbLo); + arg->ea.value = base + offset; + r->pc += 2; + return MODE_INDIRECT_X; +} +static int mode_indy(struct cpu_ctx *r, union inst_arg *arg) +{ + u8 lsbLo = MEM_ACCESS_READ(&r->mem, r->pc + 1); + u8 msbLo = lsbLo + 1; + u16 base = MEM_ACCESS_READ(&r->mem, msbLo) << 8; + u16 offset = MEM_ACCESS_READ(&r->mem, lsbLo) + r->y; + arg->ea.value = base + offset; + r->pc += 2; + r->cycles += (offset > 255); + return MODE_INDIRECT_Y; +} +static int mode_rel(struct cpu_ctx *r, union inst_arg *arg) +{ + arg->rel.value = MEM_ACCESS_READ(&r->mem, r->pc + 1); + r->pc += 2; + return MODE_RELATIVE; +} +static int mode_acc(struct cpu_ctx *r, union inst_arg *arg) +{ + r->pc += 1; + return MODE_ACCUMULATOR; +} +static int mode_imp(struct cpu_ctx *r, union inst_arg *arg) +{ + r->pc += 1; + return MODE_IMPLIED; +} + +static struct mode_info mode_imm_o = {&mode_imm, "#$%02x"}; +static struct mode_info mode_zp_o = {&mode_zp, "$%02x"}; +static struct mode_info mode_zpx_o = {&mode_zpx, "$%02x,x"}; +static struct mode_info mode_zpy_o = {&mode_zpy, "$%02x,y"}; +static struct mode_info mode_abs_o = {&mode_abs, "$%04x"}; +static struct mode_info mode_absx_o = {&mode_absx, "$%04x,x"}; +static struct mode_info mode_absy_o = {&mode_absy, "$%04x,y"}; +static struct mode_info mode_ind_o = {&mode_ind, "($%04x)"}; +static struct mode_info mode_indx_o = {&mode_indx, "($%02x,x)"}; +static struct mode_info mode_indy_o = {&mode_indy, "($%02x),y"}; +static struct mode_info mode_rel_o = {&mode_rel, "$%02x"}; +static struct mode_info mode_acc_o = {&mode_acc, "a"}; +static struct mode_info mode_imp_o = {&mode_imp, NULL}; + +static void update_flags_nz(struct cpu_ctx *r, u8 value) +{ + r->flags &= ~(FLAG_Z | FLAG_N); + r->flags |= (value == 0 ? FLAG_Z : 0) | (value & FLAG_N); +} + +static void update_carry(struct cpu_ctx *r, int bool) +{ + r->flags = (r->flags & ~FLAG_C) | (bool != 0 ? FLAG_C : 0); +} + +static void update_overflow(struct cpu_ctx *r, int bool) +{ + r->flags = (r->flags & ~FLAG_V) | (bool != 0 ? FLAG_V : 0); +} + +static u8 read_op_arg(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + switch(mode) + { + case MODE_IMMEDIATE: + value = arg->imm.value; + break; + case MODE_ACCUMULATOR: + value = r->a; + break; + default: + value = MEM_ACCESS_READ(&r->mem, arg->ea.value); + } + return value; +} + +static void write_op_arg(struct cpu_ctx *r, + int mode, union inst_arg *arg, u8 value) +{ + switch(mode) + { + case MODE_ACCUMULATOR: + r->a = value; + break; + default: + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, value); + } +} + +static void op_adc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + u16 result; + value = read_op_arg(r, mode, arg); + result = r->a + value + (r->flags & FLAG_C); + update_carry(r, result & 256); + update_overflow(r, !((r->a & 0x80) ^ (value & 0x80)) && + ((r->a & 0x80) ^ (result & 0x80))); + r->a = result; + update_flags_nz(r, r->a); +} + +static void op_and(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->a &= value; + update_flags_nz(r, r->a); +} + +static void op_asl(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + update_carry(r, value & 128); + value <<= 1; + write_op_arg(r, mode, arg, value); + update_flags_nz(r, value); +} + +static void branch(struct cpu_ctx *r, union inst_arg *arg) +{ + u16 target = r->pc + arg->rel.value; + r->cycles += 1 + ((target & ~255) != (r->pc & ~255)); + r->pc = target; +} + +static void op_bcc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(!(r->flags & FLAG_C)) + { + branch(r, arg); + } +} + +static void op_bcs(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(r->flags & FLAG_C) + { + branch(r, arg); + } +} + +static void op_beq(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(r->flags & FLAG_Z) + { + branch(r, arg); + } +} + +static void op_bit(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->flags &= ~(FLAG_N | FLAG_V | FLAG_Z); + r->flags |= value & (FLAG_N | FLAG_V); + r->flags |= (value & r->a) == 0 ? FLAG_Z : 0; +} + +static void op_bmi(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(r->flags & FLAG_N) + { + branch(r, arg); + } +} + +static void op_bne(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(!(r->flags & FLAG_Z)) + { + branch(r, arg); + } +} + +static void op_bpl(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(!(r->flags & FLAG_N)) + { + branch(r, arg); + } +} + +static void op_brk(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, (r->pc + 1) >> 8); + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->pc + 1); + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->flags | 0x10); +} + +static void op_bvc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if(!(r->flags & FLAG_V)) + { + branch(r, arg); + } +} + +static void op_bvs(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + if((r->flags & FLAG_V)) + { + branch(r, arg); + } +} + +static void op_clc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags &= ~FLAG_C; +} + +static void op_cld(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags &= ~FLAG_D; +} + +static void op_cli(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags &= ~FLAG_I; +} + +static void op_clv(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags &= ~FLAG_V; +} + +static u16 subtract(struct cpu_ctx *r, + int carry, + u8 val1, + u8 value) +{ + u16 target = val1 - value - (1 - !!carry); + update_carry(r, !(target & 256)); + update_flags_nz(r, target & 255); + return target; +} + +static void op_cmp(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + subtract(r, 1, r->a, value); +} + +static void op_cpx(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + subtract(r, 1, r->x, value); +} + +static void op_cpy(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + subtract(r, 1, r->y, value); +} + +static void op_dec(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value = MEM_ACCESS_READ(&r->mem, arg->ea.value) - 1; + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, value); + update_flags_nz(r, value); +} + +static void op_dex(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->x--; + update_flags_nz(r, r->x); +} + +static void op_dey(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->y--; + update_flags_nz(r, r->y); +} + +static void op_eor(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->a ^= value; + update_flags_nz(r, r->a); +} + +static void op_inc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value = MEM_ACCESS_READ(&r->mem, arg->ea.value) + 1; + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, value); + update_flags_nz(r, value); +} + +static void op_inx(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->x++; + update_flags_nz(r, r->x); +} + +static void op_iny(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->y++; + update_flags_nz(r, r->y); +} + +static void op_jmp(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->pc = arg->ea.value; +} + +static void op_jsr(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->pc--; + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->pc >> 8); + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->pc); + r->pc = arg->ea.value; +} + +static void op_lda(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->a = value; + update_flags_nz(r, r->a); +} + +static void op_ldx(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->x = value; + update_flags_nz(r, r->x); +} + +static void op_ldy(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->y = value; + update_flags_nz(r, r->y); +} + +static void op_lsr(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + update_carry(r, value & 1); + value >>= 1; + write_op_arg(r, mode, arg, value); + update_flags_nz(r, value); +} + +static void op_nop(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ +} + +static void op_ora(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + value = read_op_arg(r, mode, arg); + r->a |= value; + update_flags_nz(r, r->a); +} + +static void op_pha(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->a); +} + +static void op_php(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, 0x100 + r->sp--, r->flags & ~0x10); +} + +static void op_pla(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->a = MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp); + update_flags_nz(r, r->a); +} + +static void op_plp(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags = MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp); +} + +static void op_rol(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + u8 old_flags; + value = read_op_arg(r, mode, arg); + old_flags = r->flags; + update_carry(r, value & 128); + value <<= 1; + value |= (old_flags & FLAG_C) != 0 ? 1 : 0; + write_op_arg(r, mode, arg, value); + update_flags_nz(r, value); +} + +static void op_ror(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + u8 old_flags; + value = read_op_arg(r, mode, arg); + old_flags = r->flags; + update_carry(r, value & 1); + value >>= 1; + value |= (old_flags & FLAG_C) != 0 ? 128 : 0; + write_op_arg(r, mode, arg, value); + update_flags_nz(r, value); +} + +static void op_rti(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags = MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp); + r->pc = MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp); + r->pc |= MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp) << 8; +} + +static void op_rts(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->pc = MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp); + r->pc |= MEM_ACCESS_READ(&r->mem, 0x100 + ++r->sp) << 8; + r->pc += 1; +} + +static void op_sbc(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + u8 value; + u16 result; + value = read_op_arg(r, mode, arg); + result = subtract(r, r->flags & FLAG_C, r->a, value); + update_overflow(r, ((r->a & 0x80) ^ (value & 0x80)) && + ((r->a & 0x80) ^ (result & 0x80))); + r->a = result; + update_flags_nz(r, r->a); +} + +static void op_sec(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags |= FLAG_C; +} + +static void op_sed(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags |= FLAG_D; +} + +static void op_sei(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->flags |= FLAG_I; +} + +static void op_sta(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, r->a); +} + +static void op_stx(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, r->x); +} + +static void op_sty(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + MEM_ACCESS_WRITE(&r->mem, arg->ea.value, r->y); +} + +static void op_tax(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->x = r->a; + update_flags_nz(r, r->x); +} + +static void op_tay(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->y = r->a; + update_flags_nz(r, r->y); +} + +static void op_tsx(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->x = r->sp; + update_flags_nz(r, r->x); +} + +static void op_txa(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->a = r->x; + update_flags_nz(r, r->a); +} + +static void op_txs(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->sp = r->x; +} + +static void op_tya(struct cpu_ctx *r, int mode, union inst_arg *arg) +{ + r->a = r->y; + update_flags_nz(r, r->a); +} + +static struct op_info op_adc_o = {&op_adc, "adc"}; +static struct op_info op_and_o = {&op_and, "and"}; +static struct op_info op_asl_o = {&op_asl, "asl"}; +static struct op_info op_bcc_o = {&op_bcc, "bcc"}; +static struct op_info op_bcs_o = {&op_bcs, "bcs"}; +static struct op_info op_beq_o = {&op_beq, "beq"}; +static struct op_info op_bit_o = {&op_bit, "bit"}; +static struct op_info op_bmi_o = {&op_bmi, "bmi"}; +static struct op_info op_bne_o = {&op_bne, "bne"}; +static struct op_info op_bpl_o = {&op_bpl, "bpl"}; +static struct op_info op_brk_o = {&op_brk, "brk"}; +static struct op_info op_bvc_o = {&op_bvc, "bvc"}; +static struct op_info op_bvs_o = {&op_bvs, "bvs"}; +static struct op_info op_clc_o = {&op_clc, "clc"}; + +static struct op_info op_cld_o = {&op_cld, "cld"}; +static struct op_info op_cli_o = {&op_cli, "cli"}; +static struct op_info op_clv_o = {&op_clv, "clv"}; +static struct op_info op_cmp_o = {&op_cmp, "cmp"}; +static struct op_info op_cpx_o = {&op_cpx, "cpx"}; +static struct op_info op_cpy_o = {&op_cpy, "cpy"}; +static struct op_info op_dec_o = {&op_dec, "dec"}; +static struct op_info op_dex_o = {&op_dex, "dex"}; +static struct op_info op_dey_o = {&op_dey, "dey"}; +static struct op_info op_eor_o = {&op_eor, "eor"}; +static struct op_info op_inc_o = {&op_inc, "inc"}; +static struct op_info op_inx_o = {&op_inx, "inx"}; +static struct op_info op_iny_o = {&op_iny, "iny"}; +static struct op_info op_jmp_o = {&op_jmp, "jmp"}; + +static struct op_info op_jsr_o = {&op_jsr, "jsr"}; +static struct op_info op_lda_o = {&op_lda, "lda"}; +static struct op_info op_ldx_o = {&op_ldx, "ldx"}; +static struct op_info op_ldy_o = {&op_ldy, "ldy"}; +static struct op_info op_lsr_o = {&op_lsr, "lsr"}; +static struct op_info op_nop_o = {&op_nop, "nop"}; +static struct op_info op_ora_o = {&op_ora, "ora"}; +static struct op_info op_pha_o = {&op_pha, "pha"}; +static struct op_info op_php_o = {&op_php, "php"}; +static struct op_info op_pla_o = {&op_pla, "pla"}; +static struct op_info op_plp_o = {&op_plp, "plp"}; +static struct op_info op_rol_o = {&op_rol, "rol"}; +static struct op_info op_ror_o = {&op_ror, "ror"}; +static struct op_info op_rti_o = {&op_rti, "rti"}; + +static struct op_info op_rts_o = {&op_rts, "rts"}; +static struct op_info op_sbc_o = {&op_sbc, "sbc"}; +static struct op_info op_sec_o = {&op_sec, "sec"}; +static struct op_info op_sed_o = {&op_sed, "sed"}; +static struct op_info op_sei_o = {&op_sei, "sei"}; +static struct op_info op_sta_o = {&op_sta, "sta"}; +static struct op_info op_stx_o = {&op_stx, "stx"}; +static struct op_info op_sty_o = {&op_sty, "sty"}; +static struct op_info op_tax_o = {&op_tax, "tax"}; +static struct op_info op_tay_o = {&op_tay, "tay"}; +static struct op_info op_tsx_o = {&op_tsx, "tsx"}; +static struct op_info op_txa_o = {&op_txa, "txa"}; +static struct op_info op_txs_o = {&op_txs, "txs"}; +static struct op_info op_tya_o = {&op_tya, "tya"}; + +#define NULL_OP {NULL, NULL, 0} + +/* http://www.obelisk.demon.co.uk/6502/reference.html is a nice reference */ +static struct inst_info ops[256] = { + /* 0x00 */ + {&op_brk_o, &mode_imp_o, 7}, + {&op_ora_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_ora_o, &mode_zp_o, 3}, + {&op_asl_o, &mode_zp_o, 5}, + NULL_OP, + {&op_php_o, &mode_imp_o, 3}, + {&op_ora_o, &mode_imm_o, 2}, + {&op_asl_o, &mode_acc_o, 2}, + NULL_OP, + NULL_OP, + {&op_ora_o, &mode_abs_o, 4}, + {&op_asl_o, &mode_abs_o, 6}, + NULL_OP, + /* 0x10 */ + {&op_bpl_o, &mode_rel_o, 2}, + {&op_ora_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_ora_o, &mode_zpx_o, 4}, + {&op_asl_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_clc_o, &mode_imp_o, 2}, + {&op_ora_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_ora_o, &mode_absx_o, 4}, + {&op_asl_o, &mode_absx_o, 7}, + NULL_OP, + /* 0x20 */ + {&op_jsr_o, &mode_abs_o, 6}, + {&op_and_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + {&op_bit_o, &mode_zp_o, 3}, + {&op_and_o, &mode_zp_o, 3}, + {&op_rol_o, &mode_zp_o, 5}, + NULL_OP, + {&op_plp_o, &mode_imp_o, 4}, + {&op_and_o, &mode_imm_o, 2}, + {&op_rol_o, &mode_acc_o, 2}, + NULL_OP, + {&op_bit_o, &mode_abs_o, 4}, + {&op_and_o, &mode_abs_o, 4}, + {&op_rol_o, &mode_abs_o, 6}, + NULL_OP, + /* 0x30 */ + {&op_bmi_o, &mode_rel_o, 2}, + {&op_and_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_and_o, &mode_zpx_o, 4}, + {&op_rol_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_sec_o, &mode_imp_o, 2}, + {&op_and_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_and_o, &mode_absx_o, 4}, + {&op_rol_o, &mode_absx_o, 7}, + NULL_OP, + /* 0x40 */ + {&op_rti_o, &mode_imp_o, 6}, + {&op_eor_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_eor_o, &mode_zp_o, 4}, + {&op_lsr_o, &mode_zp_o, 5}, + NULL_OP, + {&op_pha_o, &mode_imp_o, 3}, + {&op_eor_o, &mode_imm_o, 2}, + {&op_lsr_o, &mode_acc_o, 2}, + NULL_OP, + {&op_jmp_o, &mode_abs_o, 3}, + {&op_eor_o, &mode_abs_o, 4}, + {&op_lsr_o, &mode_abs_o, 6}, + NULL_OP, + /* 0x50 */ + {&op_bvc_o, &mode_rel_o, 2}, + {&op_eor_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_eor_o, &mode_zpx_o, 3}, + {&op_lsr_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_cli_o, &mode_imp_o, 2}, + {&op_eor_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_eor_o, &mode_absx_o, 4}, + {&op_lsr_o, &mode_absx_o, 7}, + NULL_OP, + /* 0x60 */ + {&op_rts_o, &mode_imp_o, 6}, + {&op_adc_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_adc_o, &mode_zp_o, 3}, + {&op_ror_o, &mode_zp_o, 5}, + NULL_OP, + {&op_pla_o, &mode_imp_o, 4}, + {&op_adc_o, &mode_imm_o, 2}, + {&op_ror_o, &mode_acc_o, 2}, + {&op_jmp_o, &mode_ind_o, 5}, + NULL_OP, + {&op_adc_o, &mode_abs_o, 4}, + {&op_ror_o, &mode_abs_o, 6}, + NULL_OP, + /* 0x70 */ + {&op_bvs_o, &mode_rel_o, 2}, + {&op_adc_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_adc_o, &mode_zpx_o, 4}, + {&op_ror_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_sei_o, &mode_imp_o, 2}, + {&op_adc_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_adc_o, &mode_absx_o, 4}, + {&op_ror_o, &mode_absx_o, 7}, + NULL_OP, + /* 0x80 */ + NULL_OP, + {&op_sta_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + {&op_sty_o, &mode_zp_o, 3}, + {&op_sta_o, &mode_zp_o, 3}, + {&op_stx_o, &mode_zp_o, 3}, + NULL_OP, + {&op_dey_o, &mode_imp_o, 2}, + NULL_OP, + {&op_txa_o, &mode_imp_o, 2}, + NULL_OP, + {&op_sty_o, &mode_abs_o, 4}, + {&op_sta_o, &mode_abs_o, 4}, + {&op_stx_o, &mode_abs_o, 4}, + NULL_OP, + /* 0x90 */ + {&op_bcc_o, &mode_rel_o, 2}, + {&op_sta_o, &mode_indy_o, 6}, + NULL_OP, + NULL_OP, + {&op_sty_o, &mode_zpx_o, 4}, + {&op_sta_o, &mode_zpx_o, 4}, + {&op_stx_o, &mode_zpy_o, 4}, + NULL_OP, + {&op_tya_o, &mode_imp_o, 2}, + {&op_sta_o, &mode_absy_o, 5}, + {&op_txs_o, &mode_imp_o, 2}, + NULL_OP, + NULL_OP, + {&op_sta_o, &mode_absx_o, 5}, + NULL_OP, + NULL_OP, + /* 0xa0 */ + {&op_ldy_o, &mode_imm_o, 2}, + {&op_lda_o, &mode_indx_o, 6}, + {&op_ldx_o, &mode_imm_o, 2}, + NULL_OP, + {&op_ldy_o, &mode_zp_o, 3}, + {&op_lda_o, &mode_zp_o, 3}, + {&op_ldx_o, &mode_zp_o, 3}, + NULL_OP, + {&op_tay_o, &mode_imp_o, 2}, + {&op_lda_o, &mode_imm_o, 2}, + {&op_tax_o, &mode_imp_o, 2}, + NULL_OP, + {&op_ldy_o, &mode_abs_o, 4}, + {&op_lda_o, &mode_abs_o, 4}, + {&op_ldx_o, &mode_abs_o, 4}, + NULL_OP, + /* 0xb0 */ + {&op_bcs_o, &mode_rel_o, 2}, + {&op_lda_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + {&op_ldy_o, &mode_zpx_o, 4}, + {&op_lda_o, &mode_zpx_o, 4}, + {&op_ldx_o, &mode_zpy_o, 4}, + NULL_OP, + {&op_clv_o, &mode_imp_o, 2}, + {&op_lda_o, &mode_absy_o, 4}, + {&op_tsx_o, &mode_imp_o, 2}, + NULL_OP, + {&op_ldy_o, &mode_absx_o, 4}, + {&op_lda_o, &mode_absx_o, 4}, + {&op_ldx_o, &mode_absy_o, 4}, + NULL_OP, + /* 0xc0 */ + {&op_cpy_o, &mode_imm_o, 2}, + {&op_cmp_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + {&op_cpy_o, &mode_zp_o, 3}, + {&op_cmp_o, &mode_zp_o, 3}, + {&op_dec_o, &mode_zp_o, 5}, + NULL_OP, + {&op_iny_o, &mode_imp_o, 2}, + {&op_cmp_o, &mode_imm_o, 2}, + {&op_dex_o, &mode_imp_o, 2}, + NULL_OP, + {&op_cpy_o, &mode_abs_o, 4}, + {&op_cmp_o, &mode_abs_o, 4}, + {&op_dec_o, &mode_abs_o, 6}, + NULL_OP, + /* 0xd0 */ + {&op_bne_o, &mode_rel_o, 2}, + {&op_cmp_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_cmp_o, &mode_zpx_o, 4}, + {&op_dec_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_cld_o, &mode_imp_o, 2}, + {&op_cmp_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_cmp_o, &mode_absx_o, 4}, + {&op_dec_o, &mode_absx_o, 7}, + NULL_OP, + /* 0xe0 */ + {&op_cpx_o, &mode_imm_o, 2}, + {&op_sbc_o, &mode_indx_o, 6}, + NULL_OP, + NULL_OP, + {&op_cpx_o, &mode_zp_o, 3}, + {&op_sbc_o, &mode_zp_o, 3}, + {&op_inc_o, &mode_zp_o, 5}, + NULL_OP, + {&op_inx_o, &mode_imp_o, 2}, + {&op_sbc_o, &mode_imm_o, 2}, + {&op_nop_o, &mode_imp_o, 2}, + NULL_OP, + {&op_cpx_o, &mode_abs_o, 4}, + {&op_sbc_o, &mode_abs_o, 4}, + {&op_inc_o, &mode_abs_o, 6}, + NULL_OP, + /* 0xf0 */ + {&op_beq_o, &mode_rel_o, 2}, + {&op_sbc_o, &mode_indy_o, 5}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_sbc_o, &mode_zpx_o, 4}, + {&op_inc_o, &mode_zpx_o, 6}, + NULL_OP, + {&op_sed_o, &mode_imp_o, 2}, + {&op_sbc_o, &mode_absy_o, 4}, + NULL_OP, + NULL_OP, + NULL_OP, + {&op_sbc_o, &mode_absx_o, 4}, + {&op_inc_o, &mode_absx_o, 7}, + NULL_OP, +}; + +void next_inst(struct cpu_ctx *r) +{ + union inst_arg arg; + int oldpc = r->pc; + int op_code = MEM_ACCESS_READ(&r->mem, r->pc); + struct inst_info *info = ops + op_code; + int mode; + if(info->op == NULL) + { + LOG(LOG_ERROR, ("unimplemented opcode $%02X @ $%04X\n", + op_code, r->pc)); + exit(1); + } + LOG(LOG_DUMP, ("%08d, %02x %02x %02x %02x: ", + r->cycles, r->a, r->x, r->y, r->sp)); + mode = info->mode->f(r, &arg); + + if(IS_LOGGABLE(LOG_DUMP)) + { + int i; + int pc = oldpc; + LOG(LOG_DUMP, ("%04x", pc)); + for(i = 0; i < 3; ++i) + { + if(pc < r->pc) + { + LOG(LOG_DUMP, (" %02x", MEM_ACCESS_READ(&r->mem, pc))); + ++pc; + } + else + { + LOG(LOG_DUMP, (" ")); + } + } + LOG(LOG_DUMP, (" %s", info->op->fmt)); + + if(info->mode->fmt != NULL) + { + int value = 0; + while(--pc > oldpc) + { + value <<= 8; + value |= MEM_ACCESS_READ(&r->mem, pc); + } + LOG(LOG_DUMP, (" ")); + if(mode == MODE_RELATIVE) + { + LOG(LOG_DUMP, ("$%04x", r->pc + arg.rel.value)); + } + else + { + LOG(LOG_DUMP, (info->mode->fmt, value)); + } + switch (mode) + { + case MODE_RELATIVE: + case MODE_IMPLIED: + case MODE_ACCUMULATOR: + case MODE_IMMEDIATE: + case MODE_ABSOLUTE: + break; + default: + LOG(LOG_DUMP, (" ($%04x)", arg.ea.value)); + } + } + LOG(LOG_DUMP, ("\n")); + } + info->op->f(r, mode, &arg); + r->cycles += info->cycles; +} diff --git a/loader/tools/exomizer-3.1/src/6502emu.h b/loader/tools/exomizer-3.1/src/6502emu.h new file mode 100644 index 0000000..cee3ba7 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/6502emu.h @@ -0,0 +1,66 @@ +#ifndef ALREADY_INCLUDED_6502EMU +#define ALREADY_INCLUDED_6502EMU +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2007 - 2008 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "int.h" + +#define MEM_ACCESS_READ(THIS,ADDR) (THIS)->read((THIS),(ADDR)) +#define MEM_ACCESS_WRITE(THIS,ADDR,VALUE) (THIS)->write((THIS),(ADDR),(VALUE)) + +struct mem_access +{ + void *ctx; + u8 (*read)(struct mem_access *this, u16 address); + void (*write)(struct mem_access *this, u16 address, u8 value); +}; + +u16 mem_access_read_u16le(struct mem_access *this, u16 address); + + +struct cpu_ctx +{ + struct mem_access mem; + u32 cycles; + u16 pc; + u8 sp; + u8 flags; + u8 a; + u8 x; + u8 y; +}; + +void next_inst(struct cpu_ctx *r); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/Makefile b/loader/tools/exomizer-3.1/src/Makefile new file mode 100644 index 0000000..dc6d240 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/Makefile @@ -0,0 +1,76 @@ +# +# Makefile for exomizer +# +WFLAGS = -std=c89 -Wall -Wstrict-prototypes -D_XOPEN_SOURCE=600 -pedantic +CFLAGS = $(WFLAGS) -O3 -ffast-math -fomit-frame-pointer +LDFLAGS = -s + +#CFLAGS = -g $(WFLAGS) +#LDFLAGS = -g + +SHARED_OBJS = getflag.o log.o buf.o +RAW_OBJS = match.o search.o exo_raw.o optimal.o output.o buf_io.o \ + chunkpool.o radix.o exo_helper.o exodec.o progress.o exo_util.o \ + vec.o +EXO_OBJS = match.o search.o exo_main.o optimal.o output.o buf_io.o \ + chunkpool.o radix.o exo_helper.o exodec.o progress.o asm.tab.o \ + lex.yy.o parse.o expr.o pc.o vec.o named_buffer.o map.o desfx.o \ + 6502emu.o exo_util.o areatrace.o sfxdecr.o table.o perf.o +BAS_OBJS = bas_main.o bprg_renumber.o bprg_link_patch.o bprg_trampoline.o \ + bprg.o vec.o +ALL_OBJS = $(EXO_OBJS) $(RAW_OBJS) $(BAS_OBJS) $(SHARED_OBJS) + +#.SILENT: + +.PHONY: build clean +.INTERMEDIATE: b2buf exoraw + +build: $(MAKEFILE) exomizer exobasic + +exomizer: deps $(EXO_OBJS) $(SHARED_OBJS) + @echo "Linking $@" + @$(CC) $(LDFLAGS) -o $@ $(EXO_OBJS) $(SHARED_OBJS) + +exoraw: deps $(RAW_OBJS) $(SHARED_OBJS) + @echo "Linking $@" + @$(CC) $(LDFLAGS) -o $@ $(RAW_OBJS) $(SHARED_OBJS) + +exobasic: deps $(BAS_OBJS) $(SHARED_OBJS) + @echo "Linking $@" + @$(CC) $(LDFLAGS) -o $@ $(BAS_OBJS) $(SHARED_OBJS) + +clean: + @echo "Cleaning project" + -@$(RM) $(EXO_OBJS) $(RAW_OBJS) $(BAS_OBJS) $(SHARED_OBJS) + -@$(RM) b2buf.o b2buf b2buf.exe sfxdecr sfxdecr.c deps + -@$(RM) exomizer exoraw exobasic exomizer.exe exoraw.exe exobasic.exe + + +asm.tab.h asm.tab.c: asm.y + bison -t -d asm.y + +lex.yy.c: asm.yy + flex -B asm.yy + +sfxdecr.c: sfxdecr b2buf + @./b2buf sfxdecr >sfxdecr.c + +sfxdecr: sfxdecr.s exoraw + @echo "Compressing $<" + @./exoraw -q sfxdecr.s -o sfxdecr + +-include deps + +deps: $(wildcard *.h) asm.tab.h + @echo "Generating dependencies" + @$(CC) -MM $(wildcard *.c) >$@ + +%.o: %.c + @echo "Compiling $<" + @$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $< + +%: %.o + @$(CC) $(LDFLAGS) $< -o $@ + +tabletest: table.o tabletest.o vec.o buf.o + @$(CC) $(CFLAGS) $^ -o $@ diff --git a/loader/tools/exomizer-3.1/src/Makefile.test b/loader/tools/exomizer-3.1/src/Makefile.test new file mode 100644 index 0000000..1fa29e3 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/Makefile.test @@ -0,0 +1,132 @@ +.INTERMEDIARY: a.out vec.h.exo 6502emu.c.exo buf.h.exo test_enc +.PHONY: test% + +test: test_raw test_rawbr test_rawr test_rawbr test_mem test_memf test_level test_levelf test_rawE test_rawbE test_rawrE test_rawbrE test_memE test_memfE test_levelE test_levelfE + +test_raw: exomizer vec.h + ./exomizer raw -q -C vec.h -o vec.h.exo + ./exomizer raw -q -d vec.h.exo + cmp vec.h a.out + +test_rawb: exomizer vec.h + ./exomizer raw -q -b -C vec.h -o vec.h.exo + ./exomizer raw -q -b -d vec.h.exo + cmp vec.h a.out + +test_rawr: exomizer vec.h + ./exomizer raw -q -r -C vec.h -o vec.h.exo + ./exomizer raw -q -r -d vec.h.exo + cmp vec.h a.out + +test_rawbr: exomizer vec.h + ./exomizer raw -q -b -r -C vec.h -o vec.h.exo + ./exomizer raw -q -b -r -d vec.h.exo + cmp vec.h a.out + +test_mem: exomizer vec.h + ./exomizer mem -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -q -b -d vec.h.exo,2,-2 + cmp vec.h a.out + +test_memf: exomizer vec.h + ./exomizer mem -f -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -q -d vec.h.exo,4 + cmp vec.h a.out + +test_level: exomizer vec.h + ./exomizer level -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -q -b -r -d vec.h.exo,2 + cmp vec.h a.out + +test_levelf: exomizer vec.h + ./exomizer level -f -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -q -d vec.h.exo,2 + cmp vec.h a.out + +test_rawE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -q -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -q -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawbE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -q -b -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -q -b -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -b -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -b -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -b -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawrE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -q -r -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -q -r -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -r -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -r -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -r -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawbrE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -q -b -r -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -q -b -r -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -b -r -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -b -r -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -b -r -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_memE: exomizer vec.h 6502emu.c buf.h + ./exomizer mem -q -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer mem -q -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -b -d vec.h.exo,0,-2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -b -d 6502emu.c.exo,0,-2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -b -d buf.h.exo,0,-2 -e @test_enc + cmp buf.h a.out + +test_memfE: exomizer vec.h 6502emu.c buf.h + ./exomizer mem -q -f -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer mem -q -f -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out + +test_levelE: exomizer vec.h 6502emu.c buf.h + ./exomizer level -q -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer level -q -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -b -r -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -b -r -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -b -r -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out + +test_levelfE: exomizer vec.h 6502emu.c buf.h + ./exomizer level -q -f -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer level -q -f -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -q -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -q -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -q -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out diff --git a/loader/tools/exomizer-3.1/src/Makefile.testP16 b/loader/tools/exomizer-3.1/src/Makefile.testP16 new file mode 100644 index 0000000..f89107d --- /dev/null +++ b/loader/tools/exomizer-3.1/src/Makefile.testP16 @@ -0,0 +1,132 @@ +.INTERMEDIARY: a.out vec.h.exo 6502emu.c.exo buf.h.exo test_enc +.PHONY: test% + +test: test_raw test_rawbr test_rawr test_rawbr test_mem test_memf test_level test_levelf test_rawE test_rawbE test_rawrE test_rawbrE test_memE test_memfE test_levelE test_levelfE + +test_raw: exomizer vec.h + ./exomizer raw -P+16 -q -C vec.h -o vec.h.exo + ./exomizer raw -P+16 -q -d vec.h.exo + cmp vec.h a.out + +test_rawb: exomizer vec.h + ./exomizer raw -P+16 -q -b -C vec.h -o vec.h.exo + ./exomizer raw -P+16 -q -b -d vec.h.exo + cmp vec.h a.out + +test_rawr: exomizer vec.h + ./exomizer raw -P+16 -q -r -C vec.h -o vec.h.exo + ./exomizer raw -P+16 -q -r -d vec.h.exo + cmp vec.h a.out + +test_rawbr: exomizer vec.h + ./exomizer raw -P+16 -q -b -r -C vec.h -o vec.h.exo + ./exomizer raw -P+16 -q -b -r -d vec.h.exo + cmp vec.h a.out + +test_mem: exomizer vec.h + ./exomizer mem -P+16 -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -P+16 -q -b -d vec.h.exo,2,-2 + cmp vec.h a.out + +test_memf: exomizer vec.h + ./exomizer mem -P+16 -f -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -P+16 -q -d vec.h.exo,4 + cmp vec.h a.out + +test_level: exomizer vec.h + ./exomizer level -P+16 -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -P+16 -q -b -r -d vec.h.exo,2 + cmp vec.h a.out + +test_levelf: exomizer vec.h + ./exomizer level -P+16 -f -q -C vec.h@0x1234 -o vec.h.exo + ./exomizer raw -P+16 -q -d vec.h.exo,2 + cmp vec.h a.out + +test_rawE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -P+16 -q -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -P+16 -q -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawbE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -P+16 -q -b -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -P+16 -q -b -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -b -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -b -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -b -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawrE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -P+16 -q -r -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -P+16 -q -r -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -r -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -r -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -r -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_rawbrE: exomizer vec.h 6502emu.c buf.h + ./exomizer raw -P+16 -q -b -r -C -E vec.h 6502emu.c -o test_enc + ./exomizer raw -P+16 -q -b -r -C -E -e @test_enc -p1 buf.h -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -b -r -d vec.h.exo -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -b -r -d 6502emu.c.exo -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -b -r -d buf.h.exo -e @test_enc + cmp buf.h a.out + +test_memE: exomizer vec.h 6502emu.c buf.h + ./exomizer mem -P+16 -q -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer mem -P+16 -q -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -b -d vec.h.exo,0,-2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -b -d 6502emu.c.exo,0,-2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -b -d buf.h.exo,0,-2 -e @test_enc + cmp buf.h a.out + +test_memfE: exomizer vec.h 6502emu.c buf.h + ./exomizer mem -P+16 -q -f -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer mem -P+16 -q -f -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out + +test_levelE: exomizer vec.h 6502emu.c buf.h + ./exomizer level -P+16 -q -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer level -P+16 -q -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -b -r -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -b -r -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -b -r -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out + +test_levelfE: exomizer vec.h 6502emu.c buf.h + ./exomizer level -P+16 -q -f -C -E vec.h@0x1234 6502emu.c@0x4321 -o test_enc + ./exomizer level -P+16 -q -f -C -E -e @test_enc -p1 buf.h@0x3412 -o test_enc2 + cmp test_enc test_enc2 + ./exomizer raw -P+16 -q -d vec.h.exo,2 -e @test_enc + cmp vec.h a.out + ./exomizer raw -P+16 -q -d 6502emu.c.exo,2 -e @test_enc + cmp 6502emu.c a.out + ./exomizer raw -P+16 -q -d buf.h.exo,2 -e @test_enc + cmp buf.h a.out diff --git a/loader/tools/exomizer-3.1/src/areatrace.c b/loader/tools/exomizer-3.1/src/areatrace.c new file mode 100644 index 0000000..6903f70 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/areatrace.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "areatrace.h" + +void areatrace_init(struct areatrace *at) +{ + vec_init(&at->areas, sizeof(u16)); + buf_reserve(&at->areas.buf, 64); +} + +void areatrace_free(struct areatrace *at) +{ + vec_free(&at->areas, NULL); +} + +void areatrace_merge_overlapping(struct areatrace *at) +{ + struct vec *areas = &at->areas; + int i = 1; + int size = vec_size(areas) - 1; + + while (i < size) + { + u16 *end = vec_get(areas, i); + u16 *start = vec_get(areas, i + 1); + if (*start == *end) + { + /* merge */ + vec_remove(areas, i); + vec_remove(areas, i); + size -= 2; + } + else + { + i += 2; + } + } +} + +void areatrace_get_largest(const struct areatrace *at, int *startp, int *endp) +{ + const struct vec *areas = &at->areas; + struct vec_iterator i; + u16 *pstart; + u16 *pend; + int start = 0; + int end = 0; + int size = -1; + + for (vec_get_iterator(areas, &i); + (pstart = vec_iterator_next(&i)) != NULL;) + { + pend = vec_iterator_next(&i); + if (size < *pend - *pstart) + { + size = *pend - *pstart; + start = *pstart; + end = *pend + 1; + } + } + + if (startp != NULL) + { + *startp = start; + } + + if (endp != NULL) + { + *endp = end; + } +} + +static int areatrace_addr_cb_cmp(const void *a, const void *b) +{ + u16 ua = *(u16*)a; + u16 ub = *(u16*)b; + if (ua < ub) + { + return -1; + } + else if (ub < ua) + { + return 1; + } + else + { + return 0; + } +} + +static int areas_next_start_pos(struct vec *areas, int pos, u16 address) +{ + int result = -1; + for (pos = pos & ~1; pos < vec_size(areas); pos += 2) + { + u16 *next_addr = vec_get(areas, pos); + if (*next_addr > address + 1) + { + break; + } + if (*next_addr == address + 1) + { + result = pos; + break; + } + } + return result; +} + +static int areas_prev_end_pos(struct vec *areas, int pos, u16 address) +{ + int result = -1; + for (pos = (pos - 2) | 1; pos >= 0; pos -= 2) + { + u16 *prev_addr = vec_get(areas, pos); + if (*prev_addr < address - 1) + { + break; + } + if (*prev_addr == address - 1) + { + result = pos; + break; + } + } + return result; +} + +void areatrace_access(struct areatrace *at, u16 address) +{ + struct vec *areas = &at->areas; + int pos; + int orig_pos; + int next_start_pos; + int prev_end_pos; + + orig_pos = vec_find(areas, areatrace_addr_cb_cmp, &address); + pos = orig_pos; + if (pos < 0) + { + pos = -(pos + 2); + } + next_start_pos = areas_next_start_pos(areas, pos, address); + prev_end_pos = areas_prev_end_pos(areas, pos, address); + + if (next_start_pos != -1) + { + vec_set(areas, next_start_pos, &address); + if (prev_end_pos == -1) + { + u16 *addrp; + addrp = vec_get(areas, next_start_pos - 1); + if (addrp != NULL && *addrp >= address) + { + u16 addr = address - 1; + /* shrink */ + vec_set(areas, next_start_pos - 1, &addr); + addrp = vec_get(areas, next_start_pos - 2); + if (*addrp == address) + { + vec_remove(areas, next_start_pos - 2); + vec_remove(areas, next_start_pos - 2); + } + } + } + } + if (prev_end_pos != -1) + { + vec_set(areas, prev_end_pos, &address); + if (next_start_pos == -1) + { + u16 *addrp; + addrp = vec_get(areas, prev_end_pos + 1); + if (addrp != NULL && *addrp <= address) + { + u16 addr = address + 1; + /* shrink */ + vec_set(areas, prev_end_pos + 1, &addr); + addrp = vec_get(areas, prev_end_pos + 2); + if (*addrp == address) + { + vec_remove(areas, prev_end_pos + 1); + vec_remove(areas, prev_end_pos + 1); + } + } + } + } + if (orig_pos < 0 && (pos & 1) == 0 && + next_start_pos == -1 && prev_end_pos == -1) + { + vec_insert(areas, pos, &address); + vec_insert(areas, pos, &address); + } +} diff --git a/loader/tools/exomizer-3.1/src/areatrace.h b/loader/tools/exomizer-3.1/src/areatrace.h new file mode 100644 index 0000000..d0623b5 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/areatrace.h @@ -0,0 +1,69 @@ +#ifndef ALREADY_INCLUDED_AREATRACE +#define ALREADY_INCLUDED_AREATRACE +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "int.h" +#include "vec.h" + +struct areatrace +{ + struct vec areas; +}; + +void areatrace_init(struct areatrace *at); + +void areatrace_free(struct areatrace *at); + +/* + * Updates the given area trace with the given memory access. + */ +void areatrace_access(struct areatrace *at /* IN/OUT */, + u16 address /* IN */); + +/* + * Merges overlapping areas in the given area trace. + * Areas can have one byte overlap when it is still undecided which of the + * areas that is growing/shrinking by the access pattern. + */ +void areatrace_merge_overlapping(struct areatrace *at /* IN/OUT */); + +/* + * Gets the largest area from the given area trace. + */ +void areatrace_get_largest(const struct areatrace *at, /* IN */ + int *startp /* OUT */, + int *endp); /* OUT */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/asm.tab.c b/loader/tools/exomizer-3.1/src/asm.tab.c new file mode 100644 index 0000000..83fcca1 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/asm.tab.c @@ -0,0 +1,3068 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0.4" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +#line 28 "asm.y" /* yacc.c:339 */ + +#include "int.h" +#include "parse.h" +#include "vec.h" +#include "buf.h" +#include "log.h" +#include +#define YYERROR_VERBOSE + +static struct vec asm_atoms[1]; + +/* prototypes to silence compiler warnings */ +int yylex(void); +void yyerror(const char *s); + + +#line 83 "asm.tab.c" /* yacc.c:339 */ + +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "asm.tab.h". */ +#ifndef YY_YY_ASM_TAB_H_INCLUDED +# define YY_YY_ASM_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + INCLUDE = 258, + IF = 259, + DEFINED = 260, + MACRO = 261, + MACRO_STRING = 262, + ORG = 263, + ERROR = 264, + ECHO1 = 265, + INCBIN = 266, + INCLEN = 267, + INCWORD = 268, + RES = 269, + WORD = 270, + BYTE = 271, + LDA = 272, + LDX = 273, + LDY = 274, + STA = 275, + STX = 276, + STY = 277, + AND = 278, + ORA = 279, + EOR = 280, + ADC = 281, + SBC = 282, + CMP = 283, + CPX = 284, + CPY = 285, + TSX = 286, + TXS = 287, + PHA = 288, + PLA = 289, + PHP = 290, + PLP = 291, + SEI = 292, + CLI = 293, + NOP = 294, + TYA = 295, + TAY = 296, + TXA = 297, + TAX = 298, + CLC = 299, + SEC = 300, + RTS = 301, + CLV = 302, + CLD = 303, + SED = 304, + JSR = 305, + JMP = 306, + BEQ = 307, + BNE = 308, + BCC = 309, + BCS = 310, + BPL = 311, + BMI = 312, + BVC = 313, + BVS = 314, + INX = 315, + DEX = 316, + INY = 317, + DEY = 318, + INC = 319, + DEC = 320, + LSR = 321, + ASL = 322, + ROR = 323, + ROL = 324, + BIT = 325, + SYMBOL = 326, + STRING = 327, + LAND = 328, + LOR = 329, + LNOT = 330, + LPAREN = 331, + RPAREN = 332, + COMMA = 333, + COLON = 334, + X = 335, + Y = 336, + HASH = 337, + PLUS = 338, + MINUS = 339, + MULT = 340, + DIV = 341, + MOD = 342, + LT = 343, + GT = 344, + EQ = 345, + NEQ = 346, + ASSIGN = 347, + GUESS = 348, + NUMBER = 349, + vNEG = 350, + LABEL = 351 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED + +union YYSTYPE +{ +#line 148 "asm.y" /* yacc.c:355 */ + + i32 num; + char *str; + struct atom *atom; + struct expr *expr; + +#line 227 "asm.tab.c" /* yacc.c:355 */ +}; + +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + +#endif /* !YY_YY_ASM_TAB_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +#line 244 "asm.tab.c" /* yacc.c:358 */ + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + +#if !defined _Noreturn \ + && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +# if defined _MSC_VER && 1200 <= _MSC_VER +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 221 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 653 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 97 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 17 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 201 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 323 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 351 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 185, 185, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 199, 200, 201, 202, + 203, 205, 207, 210, 211, 213, 214, 215, 216, 217, + 218, 219, 220, 222, 223, 224, 225, 226, 228, 229, + 230, 231, 232, 234, 235, 236, 237, 238, 239, 240, + 242, 243, 244, 246, 247, 248, 250, 251, 252, 253, + 254, 255, 256, 257, 259, 260, 261, 262, 263, 264, + 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, + 277, 278, 279, 280, 281, 282, 283, 284, 286, 287, + 288, 289, 290, 291, 292, 293, 295, 296, 297, 298, + 299, 300, 301, 302, 304, 305, 306, 307, 308, 309, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, 323, 324, 325, 326, 327, 328, 329, 331, + 332, 333, 334, 335, 336, 337, 338, 339, 340, 342, + 343, 344, 345, 347, 348, 349, 350, 352, 353, 354, + 355, 357, 358, 359, 360, 361, 363, 364, 365, 366, + 367, 369, 370, 371, 372, 373, 375, 376, 377, 378, + 379, 381, 382, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 404, 405, 407, 408, 409, 410, 411, 412, 413, + 414, 416 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "INCLUDE", "IF", "DEFINED", "MACRO", + "MACRO_STRING", "ORG", "ERROR", "ECHO1", "INCBIN", "INCLEN", "INCWORD", + "RES", "WORD", "BYTE", "LDA", "LDX", "LDY", "STA", "STX", "STY", "AND", + "ORA", "EOR", "ADC", "SBC", "CMP", "CPX", "CPY", "TSX", "TXS", "PHA", + "PLA", "PHP", "PLP", "SEI", "CLI", "NOP", "TYA", "TAY", "TXA", "TAX", + "CLC", "SEC", "RTS", "CLV", "CLD", "SED", "JSR", "JMP", "BEQ", "BNE", + "BCC", "BCS", "BPL", "BMI", "BVC", "BVS", "INX", "DEX", "INY", "DEY", + "INC", "DEC", "LSR", "ASL", "ROR", "ROL", "BIT", "SYMBOL", "STRING", + "LAND", "LOR", "LNOT", "LPAREN", "RPAREN", "COMMA", "COLON", "X", "Y", + "HASH", "PLUS", "MINUS", "MULT", "DIV", "MOD", "LT", "GT", "EQ", "NEQ", + "ASSIGN", "GUESS", "NUMBER", "vNEG", "LABEL", "$accept", "stmts", "stmt", + "atom", "exprs", "op", "am_im", "am_a", "am_ax", "am_ay", "am_zp", + "am_zpx", "am_zpy", "am_ix", "am_iy", "expr", "lexpr", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351 +}; +# endif + +#define YYPACT_NINF -214 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-214))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 290, -71, -16, -14, -214, -10, -4, 15, 82, 92, + 101, 103, 186, 189, 350, 369, 372, 383, 186, 186, + 186, 186, 186, 186, 353, 353, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, -214, -214, -214, -214, 383, + 383, 383, 383, 383, 383, 394, -35, 83, 86, -214, + -214, -214, 109, 154, 122, 148, 123, 128, 132, 148, + 148, 148, 121, 129, -214, 148, 148, 148, 148, -214, + -214, -214, -214, -214, -214, -214, -214, -214, 125, 148, + 148, -214, -214, -214, -214, -214, 508, -214, -214, -214, + -214, -214, 518, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -68, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, 148, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, 148, 148, + -214, -214, -214, 138, 140, 154, 154, 429, -18, 141, + 325, 143, -48, -13, 528, -7, -68, 7, 145, 149, + 201, -68, -214, 538, 111, 148, 148, 148, 148, 148, + 477, 548, 142, 146, -68, -68, -68, -214, 156, -214, + -63, 116, 148, 148, 148, 148, 154, 154, -214, -214, + -214, -214, -214, 148, -214, 148, 148, -214, 148, -214, + 162, 144, 150, 160, 161, -214, -214, -41, -41, -214, + -214, -214, -214, 163, 166, -214, -68, -68, -68, -68, + -214, 158, 21, 314, 482, -68, -214, 148, 164, 169, + -214, -214, -214, -214, -214, 148, -214, 493, -214, -214, + 498, -214, -214 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 111, 110, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 139, 140, 141, 142, 0, + 0, 151, 156, 161, 166, 0, 0, 0, 0, 3, + 14, 16, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 192, 0, 0, 0, 0, 191, + 25, 28, 29, 30, 26, 27, 31, 32, 174, 0, + 0, 33, 36, 37, 34, 35, 174, 38, 41, 42, + 39, 40, 174, 45, 46, 47, 43, 44, 48, 49, + 52, 50, 51, 174, 55, 53, 54, 56, 59, 60, + 61, 57, 58, 62, 63, 64, 67, 68, 69, 65, + 66, 70, 71, 72, 75, 76, 77, 73, 74, 78, + 79, 80, 83, 84, 85, 81, 82, 86, 87, 88, + 91, 92, 93, 89, 90, 94, 95, 96, 99, 100, + 101, 97, 98, 102, 103, 0, 104, 106, 105, 107, + 109, 108, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 145, 146, 143, 144, 149, 150, 147, 148, + 154, 155, 152, 153, 159, 160, 157, 158, 164, 165, + 162, 163, 169, 170, 167, 168, 172, 171, 0, 0, + 4, 1, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, + 0, 173, 187, 177, 0, 0, 0, 0, 0, 0, + 0, 177, 0, 0, 177, 5, 6, 12, 0, 195, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 13, + 8, 9, 10, 0, 20, 0, 0, 18, 0, 19, + 0, 0, 188, 0, 0, 175, 176, 182, 183, 184, + 185, 186, 188, 0, 0, 196, 197, 198, 199, 200, + 194, 193, 0, 0, 0, 23, 189, 0, 0, 0, + 178, 179, 201, 11, 21, 0, 17, 0, 181, 180, + 0, 190, 22 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -214, -214, 179, -214, -77, -214, 151, 476, 488, 165, + 18, 354, 233, 621, 630, -12, -213 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 68, 69, 70, 235, 71, 90, 91, 92, 93, + 94, 95, 105, 96, 97, 123, 228 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint16 yytable[] = +{ + 98, 106, 112, 98, 237, 72, 98, 98, 98, 98, + 98, 98, 259, 261, 292, 245, 246, 247, 248, 249, + 245, 246, 247, 248, 249, 262, 263, 264, 265, 272, + 273, 104, 110, 116, 121, 125, 131, 139, 147, 155, + 163, 171, 178, 181, 247, 248, 249, 112, 112, 112, + 112, 112, 112, 300, 301, 266, 267, 218, 219, 268, + 73, 227, 74, 230, 274, 275, 75, 234, 236, 236, + 277, 278, 76, 240, 241, 242, 243, 194, 198, 202, + 206, 210, 214, 217, 279, 278, 221, 250, 251, 1, + 2, 77, 3, 4, 5, 6, 7, 8, 313, 278, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 78, 224, + 82, 83, 220, 254, 101, 107, 82, 83, 79, 127, + 135, 143, 151, 159, 167, 176, 179, 80, 103, 81, + 115, 223, 67, 130, 138, 146, 154, 162, 170, 266, + 267, 285, 286, 295, 229, 231, 302, 238, 82, 83, + 232, 82, 83, 244, 233, 239, 255, 256, 245, 246, + 247, 248, 249, 227, 260, 257, 258, 280, 269, 84, + 271, 281, 307, 286, 99, 84, 285, 294, 308, 225, + 226, 266, 87, 287, 288, 289, 290, 291, 87, 306, + 309, 310, 89, 312, 311, 318, 319, 222, 89, 122, + 296, 297, 298, 299, 227, 227, 0, 84, 0, 0, + 84, 236, 85, 303, 304, 99, 305, 0, 86, 0, + 87, 86, 0, 87, 88, 0, 0, 100, 282, 283, + 89, 0, 0, 89, 245, 246, 247, 248, 249, 0, + 0, 0, 0, 1, 2, 317, 3, 4, 5, 6, + 7, 8, 0, 320, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 82, 83, 0, 82, 83, 0, 111, 117, + 0, 126, 132, 140, 148, 156, 164, 172, 0, 0, + 0, 82, 83, 0, 82, 83, 67, 0, 0, 0, + 0, 314, 315, 0, 0, 82, 83, 245, 246, 247, + 248, 249, 270, 0, 0, 0, 82, 83, 245, 246, + 247, 248, 249, 195, 199, 203, 207, 211, 215, 0, + 0, 84, 0, 0, 84, 0, 99, 0, 0, 99, + 0, 0, 86, 0, 87, 86, 0, 87, 88, 0, + 84, 175, 0, 84, 89, 85, 0, 89, 99, 0, + 0, 0, 0, 87, 84, 0, 87, 88, 0, 99, + 100, 0, 0, 89, 0, 84, 89, 87, 0, 0, + 99, 88, 0, 0, 0, 0, 0, 89, 87, 0, + 0, 0, 175, 0, 0, 0, 0, 0, 89, 102, + 108, 113, 120, 124, 128, 136, 144, 152, 160, 168, + 177, 180, 109, 114, 0, 0, 129, 137, 145, 153, + 161, 169, 245, 246, 247, 248, 249, 262, 263, 264, + 265, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 0, 0, 0, 0, 192, 196, 200, 204, 208, + 212, 216, 0, 0, 0, 0, 0, 193, 197, 201, + 205, 209, 213, 0, 292, 0, 0, 0, 0, 316, + 245, 246, 247, 248, 249, 245, 246, 247, 248, 249, + 321, 0, 0, 0, 0, 322, 245, 246, 247, 248, + 249, 245, 246, 247, 248, 249, 252, 0, 0, 0, + 0, 245, 246, 247, 248, 249, 253, 0, 0, 0, + 0, 245, 246, 247, 248, 249, 276, 0, 0, 0, + 0, 245, 246, 247, 248, 249, 284, 0, 0, 0, + 0, 245, 246, 247, 248, 249, 293, 0, 0, 0, + 0, 245, 246, 247, 248, 249, 118, 0, 0, 133, + 141, 149, 157, 165, 173, 119, 0, 0, 134, 142, + 150, 158, 166, 174 +}; + +static const yytype_int16 yycheck[] = +{ + 12, 13, 14, 15, 81, 76, 18, 19, 20, 21, + 22, 23, 225, 226, 77, 83, 84, 85, 86, 87, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 77, + 78, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 85, 86, 87, 59, 60, 61, + 62, 63, 64, 266, 267, 73, 74, 92, 93, 77, + 76, 73, 76, 75, 77, 78, 76, 79, 80, 81, + 77, 78, 76, 85, 86, 87, 88, 59, 60, 61, + 62, 63, 64, 65, 77, 78, 0, 99, 100, 3, + 4, 76, 6, 7, 8, 9, 10, 11, 77, 78, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 76, 5, + 12, 13, 79, 175, 13, 14, 12, 13, 76, 18, + 19, 20, 21, 22, 23, 24, 25, 76, 13, 76, + 15, 72, 96, 18, 19, 20, 21, 22, 23, 73, + 74, 80, 81, 77, 72, 72, 273, 76, 12, 13, + 72, 12, 13, 78, 72, 76, 218, 219, 83, 84, + 85, 86, 87, 225, 226, 77, 76, 72, 77, 71, + 77, 72, 78, 81, 76, 71, 80, 71, 78, 75, + 76, 73, 84, 245, 246, 247, 248, 249, 84, 77, + 80, 80, 94, 77, 81, 81, 77, 68, 94, 16, + 262, 263, 264, 265, 266, 267, -1, 71, -1, -1, + 71, 273, 76, 275, 276, 76, 278, -1, 82, -1, + 84, 82, -1, 84, 88, -1, -1, 88, 77, 78, + 94, -1, -1, 94, 83, 84, 85, 86, 87, -1, + -1, -1, -1, 3, 4, 307, 6, 7, 8, 9, + 10, 11, -1, 315, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 12, 13, -1, 12, 13, -1, 14, 15, + -1, 17, 18, 19, 20, 21, 22, 23, -1, -1, + -1, 12, 13, -1, 12, 13, 96, -1, -1, -1, + -1, 77, 78, -1, -1, 12, 13, 83, 84, 85, + 86, 87, 77, -1, -1, -1, 12, 13, 83, 84, + 85, 86, 87, 59, 60, 61, 62, 63, 64, -1, + -1, 71, -1, -1, 71, -1, 76, -1, -1, 76, + -1, -1, 82, -1, 84, 82, -1, 84, 88, -1, + 71, 88, -1, 71, 94, 76, -1, 94, 76, -1, + -1, -1, -1, 84, 71, -1, 84, 88, -1, 76, + 88, -1, -1, 94, -1, 71, 94, 84, -1, -1, + 76, 88, -1, -1, -1, -1, -1, 94, 84, -1, + -1, -1, 88, -1, -1, -1, -1, -1, 94, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 14, 15, -1, -1, 18, 19, 20, 21, + 22, 23, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, -1, -1, -1, -1, 59, 60, 61, 62, 63, + 64, 65, -1, -1, -1, -1, -1, 59, 60, 61, + 62, 63, 64, -1, 77, -1, -1, -1, -1, 77, + 83, 84, 85, 86, 87, 83, 84, 85, 86, 87, + 77, -1, -1, -1, -1, 77, 83, 84, 85, 86, + 87, 83, 84, 85, 86, 87, 78, -1, -1, -1, + -1, 83, 84, 85, 86, 87, 78, -1, -1, -1, + -1, 83, 84, 85, 86, 87, 78, -1, -1, -1, + -1, 83, 84, 85, 86, 87, 78, -1, -1, -1, + -1, 83, 84, 85, 86, 87, 78, -1, -1, -1, + -1, 83, 84, 85, 86, 87, 15, -1, -1, 18, + 19, 20, 21, 22, 23, 15, -1, -1, 18, 19, + 20, 21, 22, 23 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 4, 6, 7, 8, 9, 10, 11, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 96, 98, 99, + 100, 102, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 12, 13, 71, 76, 82, 84, 88, 94, + 103, 104, 105, 106, 107, 108, 110, 111, 112, 76, + 88, 103, 104, 106, 107, 109, 112, 103, 104, 105, + 107, 108, 112, 104, 105, 106, 107, 108, 110, 111, + 104, 107, 109, 112, 104, 107, 108, 103, 104, 105, + 106, 107, 108, 110, 111, 103, 104, 105, 106, 107, + 108, 110, 111, 103, 104, 105, 106, 107, 108, 110, + 111, 103, 104, 105, 106, 107, 108, 110, 111, 103, + 104, 105, 106, 107, 108, 110, 111, 103, 104, 105, + 106, 107, 108, 110, 111, 88, 103, 104, 107, 103, + 104, 107, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 105, 107, 108, 104, 105, 107, 108, + 104, 105, 107, 108, 104, 105, 107, 108, 104, 105, + 107, 108, 104, 105, 107, 108, 104, 107, 92, 93, + 79, 0, 99, 72, 5, 75, 76, 112, 113, 72, + 112, 72, 72, 72, 112, 101, 112, 101, 76, 76, + 112, 112, 112, 112, 78, 83, 84, 85, 86, 87, + 112, 112, 78, 78, 112, 112, 112, 77, 76, 113, + 112, 113, 88, 89, 90, 91, 73, 74, 77, 77, + 77, 77, 77, 78, 77, 78, 78, 77, 78, 77, + 72, 72, 77, 78, 78, 80, 81, 112, 112, 112, + 112, 112, 77, 78, 71, 77, 112, 112, 112, 112, + 113, 113, 101, 112, 112, 112, 77, 78, 78, 80, + 80, 81, 77, 77, 77, 78, 77, 112, 81, 77, + 112, 77, 77 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 97, 98, 98, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, + 100, 100, 100, 101, 101, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, + 113, 113 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 1, 2, 3, 3, 4, 4, 4, + 4, 6, 4, 4, 1, 1, 1, 6, 4, 4, + 4, 6, 8, 3, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, + 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, + 2, 2, 2, 2, 1, 3, 3, 2, 4, 4, + 5, 5, 3, 3, 3, 3, 3, 2, 3, 4, + 6, 1, 1, 3, 3, 2, 3, 3, 3, 3, + 3, 4 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +{ + YYUSE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 186 "asm.y" /* yacc.c:1646 */ + { new_label((yyvsp[-1].str)); } +#line 1627 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 5: +#line 187 "asm.y" /* yacc.c:1646 */ + { new_symbol_expr((yyvsp[-2].str), (yyvsp[0].expr)); } +#line 1633 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 6: +#line 188 "asm.y" /* yacc.c:1646 */ + { new_symbol_expr_guess((yyvsp[-2].str), (yyvsp[0].expr)); } +#line 1639 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 7: +#line 189 "asm.y" /* yacc.c:1646 */ + { push_if_state((yyvsp[-1].expr)); } +#line 1645 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 8: +#line 190 "asm.y" /* yacc.c:1646 */ + { set_org((yyvsp[-1].expr)); } +#line 1651 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 9: +#line 191 "asm.y" /* yacc.c:1646 */ + { asm_error((yyvsp[-1].str)); } +#line 1657 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 10: +#line 192 "asm.y" /* yacc.c:1646 */ + { asm_echo((yyvsp[-1].str), NULL); } +#line 1663 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 11: +#line 193 "asm.y" /* yacc.c:1646 */ + { asm_echo((yyvsp[-3].str), (yyvsp[-1].atom)); } +#line 1669 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 12: +#line 194 "asm.y" /* yacc.c:1646 */ + { asm_include((yyvsp[-1].str)); } +#line 1675 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 13: +#line 195 "asm.y" /* yacc.c:1646 */ + { push_macro_state((yyvsp[-1].str)); } +#line 1681 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 14: +#line 196 "asm.y" /* yacc.c:1646 */ + { vec_push(asm_atoms, &(yyvsp[0].atom)); } +#line 1687 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 15: +#line 197 "asm.y" /* yacc.c:1646 */ + { macro_append((yyvsp[0].str)); } +#line 1693 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 16: +#line 199 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = (yyvsp[0].atom); } +#line 1699 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 17: +#line 200 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_res((yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 1705 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 18: +#line 201 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = exprs_to_word_exprs((yyvsp[-1].atom)); } +#line 1711 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 19: +#line 202 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = exprs_to_byte_exprs((yyvsp[-1].atom)); } +#line 1717 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 20: +#line 203 "asm.y" /* yacc.c:1646 */ + { + (yyval.atom) = new_incbin((yyvsp[-1].str), NULL, NULL); } +#line 1724 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 21: +#line 205 "asm.y" /* yacc.c:1646 */ + { + (yyval.atom) = new_incbin((yyvsp[-3].str), (yyvsp[-1].expr), NULL); } +#line 1731 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 22: +#line 207 "asm.y" /* yacc.c:1646 */ + { + (yyval.atom) = new_incbin((yyvsp[-5].str), (yyvsp[-3].expr), (yyvsp[-1].expr)); } +#line 1738 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 23: +#line 210 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = exprs_add((yyvsp[-2].atom), (yyvsp[0].expr)); } +#line 1744 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 24: +#line 211 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_exprs((yyvsp[0].expr)); } +#line 1750 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 25: +#line 213 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA9, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 1756 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 26: +#line 214 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1762 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 27: +#line 215 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xB5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1768 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 28: +#line 216 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xAD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1774 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 29: +#line 217 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xBD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1780 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 30: +#line 218 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xB9, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1786 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 31: +#line 219 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1792 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 32: +#line 220 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xB1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1798 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 33: +#line 222 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA2, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 1804 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 34: +#line 223 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1810 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 35: +#line 224 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xB6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1816 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 36: +#line 225 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xAE, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1822 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 37: +#line 226 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xBE, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1828 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 38: +#line 228 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA0, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 1834 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 39: +#line 229 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xA4, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1840 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 40: +#line 230 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xB4, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1846 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 41: +#line 231 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xAC, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1852 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 42: +#line 232 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xBC, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1858 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 43: +#line 234 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x85, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1864 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 44: +#line 235 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x95, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1870 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 45: +#line 236 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x8D, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1876 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 46: +#line 237 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x9D, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1882 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 47: +#line 238 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x99, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1888 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 48: +#line 239 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x81, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1894 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 49: +#line 240 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x91, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1900 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 50: +#line 242 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x86, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1906 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 51: +#line 243 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x96, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1912 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 52: +#line 244 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x8e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1918 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 53: +#line 246 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x84, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1924 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 54: +#line 247 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x94, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1930 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 55: +#line 248 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x8c, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1936 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 56: +#line 250 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x29, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 1942 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 57: +#line 251 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x25, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1948 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 58: +#line 252 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x35, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1954 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 59: +#line 253 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x2d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1960 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 60: +#line 254 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x3d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1966 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 61: +#line 255 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x39, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 1972 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 62: +#line 256 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x21, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1978 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 63: +#line 257 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x31, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1984 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 64: +#line 259 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x09, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 1990 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 65: +#line 260 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x05, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 1996 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 66: +#line 261 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x15, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2002 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 67: +#line 262 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x0d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2008 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 68: +#line 263 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x1d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2014 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 69: +#line 264 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x19, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2020 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 70: +#line 265 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x01, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2026 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 71: +#line 266 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x11, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2032 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 72: +#line 268 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x49, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 2038 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 73: +#line 269 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x45, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2044 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 74: +#line 270 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x55, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2050 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 75: +#line 271 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x4d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2056 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 76: +#line 272 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x5d, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2062 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 77: +#line 273 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x59, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2068 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 78: +#line 274 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x41, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2074 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 79: +#line 275 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x51, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2080 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 80: +#line 277 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x69, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 2086 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 81: +#line 278 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x65, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2092 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 82: +#line 279 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x75, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2098 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 83: +#line 280 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x6D, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2104 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 84: +#line 281 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x7D, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2110 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 85: +#line 282 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x79, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2116 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 86: +#line 283 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x61, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2122 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 87: +#line 284 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x71, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2128 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 88: +#line 286 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe9, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 2134 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 89: +#line 287 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2140 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 90: +#line 288 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xf5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2146 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 91: +#line 289 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xeD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2152 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 92: +#line 290 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xfD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2158 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 93: +#line 291 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xf9, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2164 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 94: +#line 292 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2170 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 95: +#line 293 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xf1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2176 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 96: +#line 295 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc9, ATOM_TYPE_OP_ARG_UI8, (yyvsp[0].expr)); } +#line 2182 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 97: +#line 296 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2188 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 98: +#line 297 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xd5, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2194 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 99: +#line 298 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xcD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2200 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 100: +#line 299 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xdD, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2206 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 101: +#line 300 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xd9, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2212 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 102: +#line 301 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2218 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 103: +#line 302 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xd1, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2224 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 104: +#line 304 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe0, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2230 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 105: +#line 305 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe4, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2236 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 106: +#line 306 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xec, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2242 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 107: +#line 307 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc0, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2248 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 108: +#line 308 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc4, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2254 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 109: +#line 309 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xcc, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2260 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 110: +#line 311 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x9A); } +#line 2266 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 111: +#line 312 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xBA); } +#line 2272 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 112: +#line 313 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x48); } +#line 2278 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 113: +#line 314 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x68); } +#line 2284 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 114: +#line 315 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x08); } +#line 2290 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 115: +#line 316 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x28); } +#line 2296 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 116: +#line 317 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x78); } +#line 2302 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 117: +#line 318 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x58); } +#line 2308 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 118: +#line 319 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xea); } +#line 2314 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 119: +#line 320 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x98); } +#line 2320 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 120: +#line 321 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xa8); } +#line 2326 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 121: +#line 322 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x8a); } +#line 2332 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 122: +#line 323 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xaa); } +#line 2338 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 123: +#line 324 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x18); } +#line 2344 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 124: +#line 325 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x38); } +#line 2350 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 125: +#line 326 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x60); } +#line 2356 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 126: +#line 327 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xb8); } +#line 2362 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 127: +#line 328 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xd8); } +#line 2368 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 128: +#line 329 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xf0); } +#line 2374 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 129: +#line 331 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x20, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2380 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 130: +#line 332 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x4c, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2386 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 131: +#line 333 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xf0, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2392 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 132: +#line 334 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xd0, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2398 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 133: +#line 335 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x90, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2404 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 134: +#line 336 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xb0, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2410 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 135: +#line 337 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x10, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2416 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 136: +#line 338 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x30, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2422 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 137: +#line 339 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x50, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2428 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 138: +#line 340 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x70, ATOM_TYPE_OP_ARG_I8, (yyvsp[0].expr)); } +#line 2434 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 139: +#line 342 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xe8); } +#line 2440 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 140: +#line 343 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xca); } +#line 2446 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 141: +#line 344 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0xc8); } +#line 2452 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 142: +#line 345 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x88); } +#line 2458 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 143: +#line 347 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xe6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2464 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 144: +#line 348 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xf6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2470 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 145: +#line 349 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xee, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2476 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 146: +#line 350 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xfe, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2482 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 147: +#line 352 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xc6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2488 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 148: +#line 353 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xd6, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2494 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 149: +#line 354 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xce, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2500 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 150: +#line 355 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0xde, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2506 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 151: +#line 357 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x4a); } +#line 2512 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 152: +#line 358 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x46, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2518 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 153: +#line 359 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x56, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2524 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 154: +#line 360 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x4e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2530 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 155: +#line 361 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x5e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2536 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 156: +#line 363 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x0a); } +#line 2542 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 157: +#line 364 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x06, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2548 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 158: +#line 365 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x16, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2554 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 159: +#line 366 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x0e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2560 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 160: +#line 367 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x1e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2566 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 161: +#line 369 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x6a); } +#line 2572 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 162: +#line 370 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x66, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2578 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 163: +#line 371 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x76, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2584 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 164: +#line 372 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x6e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2590 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 165: +#line 373 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x7e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2596 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 166: +#line 375 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op0(0x2a); } +#line 2602 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 167: +#line 376 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x26, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2608 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 168: +#line 377 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x36, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2614 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 169: +#line 378 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x2e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2620 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 170: +#line 379 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x3e, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2626 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 171: +#line 381 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x24, ATOM_TYPE_OP_ARG_U8, (yyvsp[0].expr)); } +#line 2632 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 172: +#line 382 "asm.y" /* yacc.c:1646 */ + { (yyval.atom) = new_op(0x2c, ATOM_TYPE_OP_ARG_U16, (yyvsp[0].expr)); } +#line 2638 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 173: +#line 384 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[0].expr); } +#line 2644 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 174: +#line 385 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[0].expr); } +#line 2650 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 175: +#line 386 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-2].expr); } +#line 2656 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 176: +#line 387 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-2].expr); } +#line 2662 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 177: +#line 388 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[0].expr); } +#line 2668 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 178: +#line 389 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-2].expr); } +#line 2674 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 179: +#line 390 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-2].expr); } +#line 2680 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 180: +#line 391 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-3].expr); } +#line 2686 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 181: +#line 392 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-3].expr); } +#line 2692 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 182: +#line 394 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(PLUS, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2698 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 183: +#line 395 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(MINUS, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2704 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 184: +#line 396 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(MULT, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2710 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 185: +#line 397 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(DIV, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2716 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 186: +#line 398 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(MOD, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2722 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 187: +#line 399 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op1(vNEG, (yyvsp[0].expr)); } +#line 2728 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 188: +#line 400 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2734 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 189: +#line 401 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_inclen((yyvsp[-1].str)); } +#line 2740 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 190: +#line 402 "asm.y" /* yacc.c:1646 */ + { + (yyval.expr) = new_expr_incword((yyvsp[-3].str), (yyvsp[-1].expr)); } +#line 2747 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 191: +#line 404 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_number((yyvsp[0].num)); } +#line 2753 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 192: +#line 405 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_symref((yyvsp[0].str)); } +#line 2759 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 193: +#line 407 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(LOR, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2765 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 194: +#line 408 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(LAND, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2771 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 195: +#line 409 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op1(LNOT, (yyvsp[0].expr)); } +#line 2777 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 196: +#line 410 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = (yyvsp[-1].expr); } +#line 2783 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 197: +#line 411 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(LT, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2789 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 198: +#line 412 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(GT, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2795 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 199: +#line 413 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(EQ, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2801 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 200: +#line 414 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_expr_op2(NEQ, (yyvsp[-2].expr), (yyvsp[0].expr)); } +#line 2807 "asm.tab.c" /* yacc.c:1646 */ + break; + + case 201: +#line 416 "asm.y" /* yacc.c:1646 */ + { (yyval.expr) = new_is_defined((yyvsp[-1].str)); } +#line 2813 "asm.tab.c" /* yacc.c:1646 */ + break; + + +#line 2817 "asm.tab.c" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 418 "asm.y" /* yacc.c:1906 */ + + +void yyerror (const char *s) +{ + fprintf (stderr, "line %d, %s\n", num_lines, s); +} + +void asm_set_source(struct buf *buffer); + +int assembleSinglePass(struct buf *source, struct buf *dest) +{ + int val; + + yydebug = 0; + asm_src_buffer_push(source); + vec_init(asm_atoms, sizeof(struct atom*)); + val = yyparse(); + if(val == 0) + { + output_atoms(dest, asm_atoms); + } + vec_free(asm_atoms, NULL); + return val; +} diff --git a/loader/tools/exomizer-3.1/src/asm.tab.h b/loader/tools/exomizer-3.1/src/asm.tab.h new file mode 100644 index 0000000..695fb6c --- /dev/null +++ b/loader/tools/exomizer-3.1/src/asm.tab.h @@ -0,0 +1,170 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_ASM_TAB_H_INCLUDED +# define YY_YY_ASM_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + INCLUDE = 258, + IF = 259, + DEFINED = 260, + MACRO = 261, + MACRO_STRING = 262, + ORG = 263, + ERROR = 264, + ECHO1 = 265, + INCBIN = 266, + INCLEN = 267, + INCWORD = 268, + RES = 269, + WORD = 270, + BYTE = 271, + LDA = 272, + LDX = 273, + LDY = 274, + STA = 275, + STX = 276, + STY = 277, + AND = 278, + ORA = 279, + EOR = 280, + ADC = 281, + SBC = 282, + CMP = 283, + CPX = 284, + CPY = 285, + TSX = 286, + TXS = 287, + PHA = 288, + PLA = 289, + PHP = 290, + PLP = 291, + SEI = 292, + CLI = 293, + NOP = 294, + TYA = 295, + TAY = 296, + TXA = 297, + TAX = 298, + CLC = 299, + SEC = 300, + RTS = 301, + CLV = 302, + CLD = 303, + SED = 304, + JSR = 305, + JMP = 306, + BEQ = 307, + BNE = 308, + BCC = 309, + BCS = 310, + BPL = 311, + BMI = 312, + BVC = 313, + BVS = 314, + INX = 315, + DEX = 316, + INY = 317, + DEY = 318, + INC = 319, + DEC = 320, + LSR = 321, + ASL = 322, + ROR = 323, + ROL = 324, + BIT = 325, + SYMBOL = 326, + STRING = 327, + LAND = 328, + LOR = 329, + LNOT = 330, + LPAREN = 331, + RPAREN = 332, + COMMA = 333, + COLON = 334, + X = 335, + Y = 336, + HASH = 337, + PLUS = 338, + MINUS = 339, + MULT = 340, + DIV = 341, + MOD = 342, + LT = 343, + GT = 344, + EQ = 345, + NEQ = 346, + ASSIGN = 347, + GUESS = 348, + NUMBER = 349, + vNEG = 350, + LABEL = 351 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED + +union YYSTYPE +{ +#line 148 "asm.y" /* yacc.c:1909 */ + + i32 num; + char *str; + struct atom *atom; + struct expr *expr; + +#line 158 "asm.tab.h" /* yacc.c:1909 */ +}; + +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + +#endif /* !YY_YY_ASM_TAB_H_INCLUDED */ diff --git a/loader/tools/exomizer-3.1/src/asm.y b/loader/tools/exomizer-3.1/src/asm.y new file mode 100644 index 0000000..c1eb5a5 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/asm.y @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +%{ +#include "int.h" +#include "parse.h" +#include "vec.h" +#include "buf.h" +#include "log.h" +#include +#define YYERROR_VERBOSE + +static struct vec asm_atoms[1]; + +/* prototypes to silence compiler warnings */ +int yylex(void); +void yyerror(const char *s); + +%} + +%expect 5 + +%token INCLUDE +%token IF +%token DEFINED +%token MACRO +%token MACRO_STRING +%token ORG +%token ERROR +%token ECHO1 +%token INCBIN +%token INCLEN +%token INCWORD +%token RES +%token WORD +%token BYTE + +%token LDA +%token LDX +%token LDY +%token STA +%token STX +%token STY +%token AND +%token ORA +%token EOR +%token ADC +%token SBC +%token CMP +%token CPX +%token CPY + +%token TSX +%token TXS +%token PHA +%token PLA +%token PHP +%token PLP +%token SEI +%token CLI +%token NOP +%token TYA +%token TAY +%token TXA +%token TAX +%token CLC +%token SEC +%token RTS +%token CLV +%token CLD +%token SED + +%token JSR +%token JMP +%token BEQ +%token BNE +%token BCC +%token BCS +%token BPL +%token BMI +%token BVC +%token BVS +%token INX +%token DEX +%token INY +%token DEY +%token INC +%token DEC + +%token LSR +%token ASL +%token ROR +%token ROL +%token BIT + +%token SYMBOL +%token STRING + +%token LAND +%token LOR +%token LNOT + +%token LPAREN +%token RPAREN +%token COMMA +%token COLON +%token X +%token Y +%token HASH +%token PLUS +%token MINUS +%token MULT +%token DIV +%token MOD +%token LT +%token GT +%token EQ +%token NEQ +%token ASSIGN +%token GUESS +%token NUMBER + +%union +{ + i32 num; + char *str; + struct atom *atom; + struct expr *expr; +} + +%right ASSIGN +%right GUESS +%left LOR +%left LAND +%nonassoc LT GT EQ +%left MINUS PLUS +%left MULT DIV MOD +%left vNEG +%left LNOT + +%token LABEL + +%type lexpr +%type expr +%type op; +%type atom; +%type exprs; + +%type am_im; +%type am_a; +%type am_ax; +%type am_ay; +%type am_zp; +%type am_zpx; +%type am_zpy; +%type am_ix; +%type am_iy; + +%% + +stmts: stmts stmt | stmt; +stmt: LABEL COLON { new_label($1); } | + SYMBOL ASSIGN expr { new_symbol_expr($1, $3); } | + SYMBOL GUESS expr { new_symbol_expr_guess($1, $3); } | + IF LPAREN lexpr RPAREN { push_if_state($3); } | + ORG LPAREN expr RPAREN { set_org($3); } | + ERROR LPAREN STRING RPAREN { asm_error($3); } | + ECHO1 LPAREN STRING RPAREN { asm_echo($3, NULL); } | + ECHO1 LPAREN STRING COMMA exprs RPAREN { asm_echo($3, $5); } | + INCLUDE LPAREN STRING RPAREN { asm_include($3); } | + MACRO LPAREN STRING RPAREN { push_macro_state($3); } | + atom { vec_push(asm_atoms, &$1); } | + MACRO_STRING { macro_append($1); }; + +atom: op { $$ = $1; } | + RES LPAREN expr COMMA expr RPAREN { $$ = new_res($3, $5); } | + WORD LPAREN exprs RPAREN { $$ = exprs_to_word_exprs($3); } | + BYTE LPAREN exprs RPAREN { $$ = exprs_to_byte_exprs($3); } | + INCBIN LPAREN STRING RPAREN { + $$ = new_incbin($3, NULL, NULL); } | + INCBIN LPAREN STRING COMMA expr RPAREN { + $$ = new_incbin($3, $5, NULL); } | + INCBIN LPAREN STRING COMMA expr COMMA expr RPAREN { + $$ = new_incbin($3, $5, $7); }; + +exprs: exprs COMMA expr { $$ = exprs_add($1, $3); } | + expr { $$ = new_exprs($1); }; + +op: LDA am_im { $$ = new_op(0xA9, ATOM_TYPE_OP_ARG_UI8, $2); } | + LDA am_zp { $$ = new_op(0xA5, ATOM_TYPE_OP_ARG_U8, $2); } | + LDA am_zpx { $$ = new_op(0xB5, ATOM_TYPE_OP_ARG_U8, $2); } | + LDA am_a { $$ = new_op(0xAD, ATOM_TYPE_OP_ARG_U16, $2); } | + LDA am_ax { $$ = new_op(0xBD, ATOM_TYPE_OP_ARG_U16, $2); } | + LDA am_ay { $$ = new_op(0xB9, ATOM_TYPE_OP_ARG_U16, $2); } | + LDA am_ix { $$ = new_op(0xA1, ATOM_TYPE_OP_ARG_U8, $2); } | + LDA am_iy { $$ = new_op(0xB1, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: LDX am_im { $$ = new_op(0xA2, ATOM_TYPE_OP_ARG_UI8, $2); } | + LDX am_zp { $$ = new_op(0xA6, ATOM_TYPE_OP_ARG_U8, $2); } | + LDX am_zpy { $$ = new_op(0xB6, ATOM_TYPE_OP_ARG_U8, $2); } | + LDX am_a { $$ = new_op(0xAE, ATOM_TYPE_OP_ARG_U16, $2); } | + LDX am_ay { $$ = new_op(0xBE, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: LDY am_im { $$ = new_op(0xA0, ATOM_TYPE_OP_ARG_UI8, $2); } | + LDY am_zp { $$ = new_op(0xA4, ATOM_TYPE_OP_ARG_U8, $2); } | + LDY am_zpx { $$ = new_op(0xB4, ATOM_TYPE_OP_ARG_U8, $2); } | + LDY am_a { $$ = new_op(0xAC, ATOM_TYPE_OP_ARG_U16, $2); } | + LDY am_ax { $$ = new_op(0xBC, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: STA am_zp { $$ = new_op(0x85, ATOM_TYPE_OP_ARG_U8, $2); } | + STA am_zpx { $$ = new_op(0x95, ATOM_TYPE_OP_ARG_U8, $2); } | + STA am_a { $$ = new_op(0x8D, ATOM_TYPE_OP_ARG_U16, $2); } | + STA am_ax { $$ = new_op(0x9D, ATOM_TYPE_OP_ARG_U16, $2); } | + STA am_ay { $$ = new_op(0x99, ATOM_TYPE_OP_ARG_U16, $2); } | + STA am_ix { $$ = new_op(0x81, ATOM_TYPE_OP_ARG_U8, $2); } | + STA am_iy { $$ = new_op(0x91, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: STX am_zp { $$ = new_op(0x86, ATOM_TYPE_OP_ARG_U8, $2); } | + STX am_zpy { $$ = new_op(0x96, ATOM_TYPE_OP_ARG_U8, $2); } | + STX am_a { $$ = new_op(0x8e, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: STY am_zp { $$ = new_op(0x84, ATOM_TYPE_OP_ARG_U8, $2); } | + STY am_zpx { $$ = new_op(0x94, ATOM_TYPE_OP_ARG_U8, $2); } | + STY am_a { $$ = new_op(0x8c, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: AND am_im { $$ = new_op(0x29, ATOM_TYPE_OP_ARG_UI8, $2); } | + AND am_zp { $$ = new_op(0x25, ATOM_TYPE_OP_ARG_U8, $2); } | + AND am_zpx { $$ = new_op(0x35, ATOM_TYPE_OP_ARG_U8, $2); } | + AND am_a { $$ = new_op(0x2d, ATOM_TYPE_OP_ARG_U16, $2); } | + AND am_ax { $$ = new_op(0x3d, ATOM_TYPE_OP_ARG_U16, $2); } | + AND am_ay { $$ = new_op(0x39, ATOM_TYPE_OP_ARG_U16, $2); } | + AND am_ix { $$ = new_op(0x21, ATOM_TYPE_OP_ARG_U8, $2); } | + AND am_iy { $$ = new_op(0x31, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: ORA am_im { $$ = new_op(0x09, ATOM_TYPE_OP_ARG_UI8, $2); } | + ORA am_zp { $$ = new_op(0x05, ATOM_TYPE_OP_ARG_U8, $2); } | + ORA am_zpx { $$ = new_op(0x15, ATOM_TYPE_OP_ARG_U8, $2); } | + ORA am_a { $$ = new_op(0x0d, ATOM_TYPE_OP_ARG_U16, $2); } | + ORA am_ax { $$ = new_op(0x1d, ATOM_TYPE_OP_ARG_U16, $2); } | + ORA am_ay { $$ = new_op(0x19, ATOM_TYPE_OP_ARG_U16, $2); } | + ORA am_ix { $$ = new_op(0x01, ATOM_TYPE_OP_ARG_U8, $2); } | + ORA am_iy { $$ = new_op(0x11, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: EOR am_im { $$ = new_op(0x49, ATOM_TYPE_OP_ARG_UI8, $2); } | + EOR am_zp { $$ = new_op(0x45, ATOM_TYPE_OP_ARG_U8, $2); } | + EOR am_zpx { $$ = new_op(0x55, ATOM_TYPE_OP_ARG_U8, $2); } | + EOR am_a { $$ = new_op(0x4d, ATOM_TYPE_OP_ARG_U16, $2); } | + EOR am_ax { $$ = new_op(0x5d, ATOM_TYPE_OP_ARG_U16, $2); } | + EOR am_ay { $$ = new_op(0x59, ATOM_TYPE_OP_ARG_U16, $2); } | + EOR am_ix { $$ = new_op(0x41, ATOM_TYPE_OP_ARG_U8, $2); } | + EOR am_iy { $$ = new_op(0x51, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: ADC am_im { $$ = new_op(0x69, ATOM_TYPE_OP_ARG_UI8, $2); } | + ADC am_zp { $$ = new_op(0x65, ATOM_TYPE_OP_ARG_U8, $2); } | + ADC am_zpx { $$ = new_op(0x75, ATOM_TYPE_OP_ARG_U8, $2); } | + ADC am_a { $$ = new_op(0x6D, ATOM_TYPE_OP_ARG_U16, $2); } | + ADC am_ax { $$ = new_op(0x7D, ATOM_TYPE_OP_ARG_U16, $2); } | + ADC am_ay { $$ = new_op(0x79, ATOM_TYPE_OP_ARG_U16, $2); } | + ADC am_ix { $$ = new_op(0x61, ATOM_TYPE_OP_ARG_U8, $2); } | + ADC am_iy { $$ = new_op(0x71, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: SBC am_im { $$ = new_op(0xe9, ATOM_TYPE_OP_ARG_UI8, $2); } | + SBC am_zp { $$ = new_op(0xe5, ATOM_TYPE_OP_ARG_U8, $2); } | + SBC am_zpx { $$ = new_op(0xf5, ATOM_TYPE_OP_ARG_U8, $2); } | + SBC am_a { $$ = new_op(0xeD, ATOM_TYPE_OP_ARG_U16, $2); } | + SBC am_ax { $$ = new_op(0xfD, ATOM_TYPE_OP_ARG_U16, $2); } | + SBC am_ay { $$ = new_op(0xf9, ATOM_TYPE_OP_ARG_U16, $2); } | + SBC am_ix { $$ = new_op(0xe1, ATOM_TYPE_OP_ARG_U8, $2); } | + SBC am_iy { $$ = new_op(0xf1, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: CMP am_im { $$ = new_op(0xc9, ATOM_TYPE_OP_ARG_UI8, $2); } | + CMP am_zp { $$ = new_op(0xc5, ATOM_TYPE_OP_ARG_U8, $2); } | + CMP am_zpx { $$ = new_op(0xd5, ATOM_TYPE_OP_ARG_U8, $2); } | + CMP am_a { $$ = new_op(0xcD, ATOM_TYPE_OP_ARG_U16, $2); } | + CMP am_ax { $$ = new_op(0xdD, ATOM_TYPE_OP_ARG_U16, $2); } | + CMP am_ay { $$ = new_op(0xd9, ATOM_TYPE_OP_ARG_U16, $2); } | + CMP am_ix { $$ = new_op(0xc1, ATOM_TYPE_OP_ARG_U8, $2); } | + CMP am_iy { $$ = new_op(0xd1, ATOM_TYPE_OP_ARG_U8, $2); }; + +op: CPX am_im { $$ = new_op(0xe0, ATOM_TYPE_OP_ARG_U8, $2); } | + CPX am_zp { $$ = new_op(0xe4, ATOM_TYPE_OP_ARG_U8, $2); } | + CPX am_a { $$ = new_op(0xec, ATOM_TYPE_OP_ARG_U16, $2); } | + CPY am_im { $$ = new_op(0xc0, ATOM_TYPE_OP_ARG_U8, $2); } | + CPY am_zp { $$ = new_op(0xc4, ATOM_TYPE_OP_ARG_U8, $2); } | + CPY am_a { $$ = new_op(0xcc, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: TXS { $$ = new_op0(0x9A); } | + TSX { $$ = new_op0(0xBA); } | + PHA { $$ = new_op0(0x48); } | + PLA { $$ = new_op0(0x68); } | + PHP { $$ = new_op0(0x08); } | + PLP { $$ = new_op0(0x28); } | + SEI { $$ = new_op0(0x78); } | + CLI { $$ = new_op0(0x58); } | + NOP { $$ = new_op0(0xea); } | + TYA { $$ = new_op0(0x98); } | + TAY { $$ = new_op0(0xa8); } | + TXA { $$ = new_op0(0x8a); } | + TAX { $$ = new_op0(0xaa); } | + CLC { $$ = new_op0(0x18); } | + SEC { $$ = new_op0(0x38); } | + RTS { $$ = new_op0(0x60); } | + CLV { $$ = new_op0(0xb8); } | + CLD { $$ = new_op0(0xd8); } | + SED { $$ = new_op0(0xf0); }; + +op: JSR am_a { $$ = new_op(0x20, ATOM_TYPE_OP_ARG_U16, $2); } | + JMP am_a { $$ = new_op(0x4c, ATOM_TYPE_OP_ARG_U16, $2); } | + BEQ am_a { $$ = new_op(0xf0, ATOM_TYPE_OP_ARG_I8, $2); } | + BNE am_a { $$ = new_op(0xd0, ATOM_TYPE_OP_ARG_I8, $2); } | + BCC am_a { $$ = new_op(0x90, ATOM_TYPE_OP_ARG_I8, $2); } | + BCS am_a { $$ = new_op(0xb0, ATOM_TYPE_OP_ARG_I8, $2); } | + BPL am_a { $$ = new_op(0x10, ATOM_TYPE_OP_ARG_I8, $2); } | + BMI am_a { $$ = new_op(0x30, ATOM_TYPE_OP_ARG_I8, $2); } | + BVC am_a { $$ = new_op(0x50, ATOM_TYPE_OP_ARG_I8, $2); } | + BVS am_a { $$ = new_op(0x70, ATOM_TYPE_OP_ARG_I8, $2); }; + +op: INX { $$ = new_op0(0xe8); } | + DEX { $$ = new_op0(0xca); } | + INY { $$ = new_op0(0xc8); } | + DEY { $$ = new_op0(0x88); }; + +op: INC am_zp { $$ = new_op(0xe6, ATOM_TYPE_OP_ARG_U8, $2); } | + INC am_zpx { $$ = new_op(0xf6, ATOM_TYPE_OP_ARG_U8, $2); } | + INC am_a { $$ = new_op(0xee, ATOM_TYPE_OP_ARG_U16, $2); } | + INC am_ax { $$ = new_op(0xfe, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: DEC am_zp { $$ = new_op(0xc6, ATOM_TYPE_OP_ARG_U8, $2); } | + DEC am_zpx { $$ = new_op(0xd6, ATOM_TYPE_OP_ARG_U8, $2); } | + DEC am_a { $$ = new_op(0xce, ATOM_TYPE_OP_ARG_U16, $2); } | + DEC am_ax { $$ = new_op(0xde, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: LSR { $$ = new_op0(0x4a); } | + LSR am_zp { $$ = new_op(0x46, ATOM_TYPE_OP_ARG_U8, $2); } | + LSR am_zpx { $$ = new_op(0x56, ATOM_TYPE_OP_ARG_U8, $2); } | + LSR am_a { $$ = new_op(0x4e, ATOM_TYPE_OP_ARG_U16, $2); } | + LSR am_ax { $$ = new_op(0x5e, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: ASL { $$ = new_op0(0x0a); } | + ASL am_zp { $$ = new_op(0x06, ATOM_TYPE_OP_ARG_U8, $2); } | + ASL am_zpx { $$ = new_op(0x16, ATOM_TYPE_OP_ARG_U8, $2); } | + ASL am_a { $$ = new_op(0x0e, ATOM_TYPE_OP_ARG_U16, $2); } | + ASL am_ax { $$ = new_op(0x1e, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: ROR { $$ = new_op0(0x6a); } | + ROR am_zp { $$ = new_op(0x66, ATOM_TYPE_OP_ARG_U8, $2); } | + ROR am_zpx { $$ = new_op(0x76, ATOM_TYPE_OP_ARG_U8, $2); } | + ROR am_a { $$ = new_op(0x6e, ATOM_TYPE_OP_ARG_U16, $2); } | + ROR am_ax { $$ = new_op(0x7e, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: ROL { $$ = new_op0(0x2a); } | + ROL am_zp { $$ = new_op(0x26, ATOM_TYPE_OP_ARG_U8, $2); } | + ROL am_zpx { $$ = new_op(0x36, ATOM_TYPE_OP_ARG_U8, $2); } | + ROL am_a { $$ = new_op(0x2e, ATOM_TYPE_OP_ARG_U16, $2); } | + ROL am_ax { $$ = new_op(0x3e, ATOM_TYPE_OP_ARG_U16, $2); }; + +op: BIT am_zp { $$ = new_op(0x24, ATOM_TYPE_OP_ARG_U8, $2); } | + BIT am_a { $$ = new_op(0x2c, ATOM_TYPE_OP_ARG_U16, $2); }; + +am_im: HASH expr { $$ = $2; }; +am_a: expr { $$ = $1; }; +am_ax: expr COMMA X { $$ = $1; }; +am_ay: expr COMMA Y { $$ = $1; }; +am_zp: LT expr { $$ = $2; }; +am_zpx: LT expr COMMA X { $$ = $2; }; +am_zpy: LT expr COMMA Y { $$ = $2; }; +am_ix: LPAREN expr COMMA X RPAREN { $$ = $2; }; +am_iy: LPAREN expr RPAREN COMMA Y { $$ = $2; }; + +expr: expr PLUS expr { $$ = new_expr_op2(PLUS, $1, $3); } | + expr MINUS expr { $$ = new_expr_op2(MINUS, $1, $3); } | + expr MULT expr { $$ = new_expr_op2(MULT, $1, $3); } | + expr DIV expr { $$ = new_expr_op2(DIV, $1, $3); } | + expr MOD expr { $$ = new_expr_op2(MOD, $1, $3); } | + MINUS expr %prec vNEG { $$ = new_expr_op1(vNEG, $2); } | + LPAREN expr RPAREN { $$ = $2; } | + INCLEN LPAREN STRING RPAREN { $$ = new_expr_inclen($3); } | + INCWORD LPAREN STRING COMMA expr RPAREN { + $$ = new_expr_incword($3, $5); } | + NUMBER { $$ = new_expr_number($1); } | + SYMBOL { $$ = new_expr_symref($1); }; + +lexpr: lexpr LOR lexpr { $$ = new_expr_op2(LOR, $1, $3); } | + lexpr LAND lexpr { $$ = new_expr_op2(LAND, $1, $3); } | + LNOT lexpr { $$ = new_expr_op1(LNOT, $2); } | + LPAREN lexpr RPAREN { $$ = $2; } | + expr LT expr { $$ = new_expr_op2(LT, $1, $3); } | + expr GT expr { $$ = new_expr_op2(GT, $1, $3); } | + expr EQ expr { $$ = new_expr_op2(EQ, $1, $3); } | + expr NEQ expr { $$ = new_expr_op2(NEQ, $1, $3); }; + +lexpr: DEFINED LPAREN SYMBOL RPAREN { $$ = new_is_defined($3); }; + +%% + +void yyerror (const char *s) +{ + fprintf (stderr, "line %d, %s\n", num_lines, s); +} + +void asm_set_source(struct buf *buffer); + +int assembleSinglePass(struct buf *source, struct buf *dest) +{ + int val; + + yydebug = 0; + asm_src_buffer_push(source); + vec_init(asm_atoms, sizeof(struct atom*)); + val = yyparse(); + if(val == 0) + { + output_atoms(dest, asm_atoms); + } + vec_free(asm_atoms, NULL); + return val; +} diff --git a/loader/tools/exomizer-3.1/src/asm.yy b/loader/tools/exomizer-3.1/src/asm.yy new file mode 100644 index 0000000..0c8c760 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/asm.yy @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +/* scanner for a simple assembler */ +%{ +#include +#include +#include "int.h" +#include "buf.h" +#include "parse.h" +#include "asm.tab.h" + +#define MAX_SRC_BUFFER_DEPTH 10 +static YY_BUFFER_STATE src_buffers[MAX_SRC_BUFFER_DEPTH]; +static int src_buffer_depth = 0; + +static char *strdupped_get(char *text); +static int strdupped_cmp(const void *a, const void *b); +static void strdupped_free(void *a); + +int num_lines = 1; +int push_state_skip = 0; +int push_state_init = 0; +int push_state_macro = 0; +struct vec strdupped[1]; + +%} + +%x SKIP SKIP_ALL QUOTED_STRING SKIP_LINE MACROO + +%option noyywrap +%option case-insensitive +%option stack + +%% + + if(push_state_init) + {push_state_init = 0; yy_push_state(INITIAL); } + if(push_state_skip) + {push_state_skip = 0; yy_push_state(SKIP); } + if(push_state_macro) + {push_state_macro = 0; yy_push_state(MACROO); } + +\.if return IF; +\.elif BEGIN(SKIP_ALL); +\.else BEGIN(SKIP); +\.endif yy_pop_state(); + +\.include return INCLUDE; +\.macro return MACRO; + +\.defined return DEFINED; +\.org return ORG; +\.error return ERROR; +\.echo return ECHO1; +\.incbin return INCBIN; +\.inclen return INCLEN; +\.incword return INCWORD; +\.res return RES; +\.word return WORD; +\.byte return BYTE; + +\" BEGIN(QUOTED_STRING); +\; BEGIN(SKIP_LINE); + +lda return LDA; +ldx return LDX; +ldy return LDY; +sta return STA; +stx return STX; +sty return STY; +and return AND; +ora return ORA; +eor return EOR; +adc return ADC; +sbc return SBC; +cmp return CMP; +cpx return CPX; +cpy return CPY; + +tsx return TSX; +txs return TXS; +pha return PHA; +pla return PLA; +php return PHP; +plp return PLP; +sei return SEI; +cli return CLI; +nop return NOP; +tya return TYA; +tay return TAY; +txa return TXA; +tax return TAX; +clc return CLC; +sec return SEC; +rts return RTS; +clv return CLV; +cld return CLD; +sed return SED; + +jsr return JSR; +jmp return JMP; +beq return BEQ; +bne return BNE; +bcc return BCC; +bcs return BCS; +bpl return BPL; +bmi return BMI; +bvc return BVC; +bvs return BVS; +inx return INX; +dex return DEX; +iny return INY; +dey return DEY; +inc return INC; +dec return DEC; +lsr return LSR; +asl return ASL; +ror return ROR; +rol return ROL; +bit return BIT; + +[0-9]+ { yylval.num = atoi(yytext); return NUMBER; } + +$[0-9a-f]+ { yylval.num = strtol(yytext + 1, NULL, 16); return NUMBER; } + +\< return LT; +\> return GT; +== return EQ; +!= return NEQ; +! return LNOT; +\&\& return LAND; +\|\| return LOR; + +\( return LPAREN; +\) return RPAREN; +\, return COMMA; +\: return COLON; +\# return HASH; +\+ return PLUS; +\- return MINUS; +\* return MULT; +\/ return DIV; +\% return MOD; + +\= return ASSIGN; +\?= return GUESS; + +x return X; +y return Y; + +[a-z][_a-z0-9]*/: { yylval.str = strdupped_get(yytext); return LABEL; } +[a-z][_a-z0-9]* { yylval.str = strdupped_get(yytext); return SYMBOL; } + +\r\n|\n ++num_lines; + +[ \t] /* eat whitespace */ + +. printf("unknown character found %s\n", yytext); + +\.if yy_push_state(SKIP_ALL); +\.elif { yy_pop_state(); return IF; } +\.else BEGIN(INITIAL); +\.endif yy_pop_state(); +\r\n|\n ++num_lines; +. + +\.endmacro yy_pop_state(); +\.+ { yylval.str = yytext; return MACRO_STRING; } +[^\.]+ { yylval.str = yytext; return MACRO_STRING; } + +\.if yy_push_state(SKIP_ALL); +\.endif yy_pop_state(); +\r\n|\n ++num_lines; +. + +[^\"]* { + /* multi-line string with correct line count */ + char *p = strdupped_get(yytext); + yylval.str = p; + while (*p != '\0') + { + if (*p++ == '\n') + { + ++num_lines; + } + } + return STRING; +} +\" BEGIN(INITIAL); + +\r\n|\n { ++num_lines; BEGIN(INITIAL); } +. + +<> { + if(--src_buffer_depth == 0) + { + yyterminate(); + } + else + { + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(src_buffers[src_buffer_depth]); + } + } + +%% + +void scanner_init(void) +{ + vec_init(strdupped, sizeof(char*)); +} + +void asm_src_buffer_push(struct buf *buffer) +{ + if(src_buffer_depth == MAX_SRC_BUFFER_DEPTH) + { + fprintf(stderr, "source buffers nested too deep\n"); + exit(1); + } + src_buffers[src_buffer_depth++] = YY_CURRENT_BUFFER; + yy_scan_bytes(buf_data(buffer), buf_size(buffer)); +} + +static char *strdupped_get(char *text) +{ + char **pp; + /*printf("get \"%s\" => ", text);*/ + if(vec_insert_uniq(strdupped, strdupped_cmp, &text, (void*)&pp)) + { + /* replace the pointer to since will be reused */ + *pp = strdup(text); + } + /*printf("%p\n", *pp);*/ + return *pp; +} + +static void strdupped_free(void *a) +{ + char *b = *(char**)a; + /*printf("free => %p \"%s\"\n", b, b);*/ + free(b); +} + +static int strdupped_cmp(const void *a, const void *b) +{ + char *c = *(char**)a; + char *d = *(char**)b; + + return strcmp(c, d); +} + +void scanner_free(void) +{ + vec_free(strdupped, strdupped_free); +} + + +void silence_warnings_about_unused_functions(void) +{ + yyunput(0, NULL); + input(); + yy_top_state(); +} diff --git a/loader/tools/exomizer-3.1/src/b2buf.c b/loader/tools/exomizer-3.1/src/b2buf.c new file mode 100644 index 0000000..119d3c1 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/b2buf.c @@ -0,0 +1,52 @@ +#include +#include + +void generate(const char *name, FILE *in, FILE *out) +{ + char buf[12]; + char *glue = ""; + int len; + int size = 0; + + fprintf(out, "static unsigned char %s_arr[] = {\n", name); + while((len = fread(buf, 1, 12, in)) > 0) + { + int col; + + size += len; + fprintf(out, " "); + for(col = 0; col < len; ++col) + { + fprintf(out, "%s0x%02x", glue, (unsigned char)buf[col]); + glue = ","; + } + fprintf(out, "\n"); + } + fprintf(out, "};\n"); + fprintf(out, "struct buf %s = {%s_arr, %d, %d};\n", + name, name, size, size); +} + +int main(int argc, char *argv[]) +{ + int i; + if(argc < 2) + { + fprintf(stderr, "Error: must give at least one input file.\n"); + } + + fprintf(stdout, "#include \"buf.h\"\n"); + for(i = 1; i < argc; ++i) + { + FILE *in = fopen(argv[i], "rb"); + if(in == NULL) + { + fprintf(stderr, "Error: can't open file \"%s\" for input.\n", + argv[i]); + exit(1); + } + generate(argv[i], in, stdout); + fclose(in); + } + return 0; +} diff --git a/loader/tools/exomizer-3.1/src/bas_main.c b/loader/tools/exomizer-3.1/src/bas_main.c new file mode 100644 index 0000000..0cacd54 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bas_main.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "getflag.h" +#include "bprg.h" + +int +read_int_sequence(const char *str, int num, int value_vector[]) +{ + int i = 0; + + for(;;) + { + int value; + int base = 0; + char *str_end; + if(*str == '$') + { + ++str; + base = 16; + } + LOG(LOG_DEBUG, ("strtol %s\n", str)); + value = strtol(str, &str_end, base); + if(str == str_end) + { + /* conversion failed somehow */ + i = -i - 1; + break; + } + LOG(LOG_DEBUG, ("vv[%d] = %d\n", i, value)); + + value_vector[i++] = value; + + str = str_end; + if(*str == '\0') + { + /* end of sequence */ + break; + } + if(i >= num) + { + /* too many entries in sequence, value_vector is too small */ + i = -i; + break; + } + if(*str != ',') + { + /* garbage characters in sequence */ + i = -i; + break; + } + ++str; + } + LOG(LOG_DEBUG, ("returning %d\n", i)); + return i; +} + +FILE * +open_file(const char *name, int *load_addr) +{ + FILE * in; + int relocated = 0; + int reloc = 0; + int load; + + do { + char *load_str; + + in = fopen(name, "rb"); + if (in != NULL) + { + /* we have succeded in opening the file */ + break; + } + + /* hmm, let's see if the user is trying to relocate it */ + load_str = strrchr(name, ','); + if (load_str == NULL) + { + /* we fail */ + break; + } + + *load_str = '\0'; + ++load_str; + relocated = 1; + + /* relocation was requested */ + if (read_int_sequence(load_str, 1, &reloc) < 0) + { + /* we fail */ + LOG(LOG_ERROR, + (" can't parse load address from \"%s\"\n", load_str)); + exit(1); + } + + in = fopen(name, "rb"); + + } while (0); + if (in == NULL) + { + LOG(LOG_ERROR, + (" can't open file \"%s\" for input\n", name)); + exit(1); + } + + /* set the load address */ + load = fgetc(in); + load |= fgetc(in) << 8; + + if(load_addr != NULL) + { + *load_addr = load; + if (relocated) + { + *load_addr = reloc; + } + } + LOG(LOG_DEBUG, ("opened file \"%s\" for load at $%04X.\n", + name, *load_addr)); + return in; +} + +void print_license(void) +{ + LOG(LOG_WARNING, + ("----------------------------------------------------------------------------\n" + "Exobasic v1.0b2, Copyright (c) 2003 Magnus Lind. (magli143@gmail.com)\n" + "----------------------------------------------------------------------------\n")); + LOG(LOG_WARNING, + ("This software is provided 'as-is', without any express or implied warranty.\n" + "In no event will the authors be held liable for any damages arising from\n" + "the use of this software.\n" + "Permission is granted to anyone to use this software, alter it and re-\n" + "distribute it freely for any non-commercial, non-profit purpose subject to\n" + "the following restrictions:\n\n")); + LOG(LOG_WARNING, + (" 1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software in a\n" + " product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\n" + " 2. Altered source versions must be plainly marked as such, and must not\n" + " be misrepresented as being the original software.\n" + " 3. This notice may not be removed or altered from any distribution.\n")); + LOG(LOG_WARNING, + (" 4. The names of this software and/or it's copyright holders may not be\n" + " used to endorse or promote products derived from this software without\n" + " specific prior written permission.\n" + "----------------------------------------------------------------------------\n" + "The files processed and/or generated by using this software are not covered\n" + "nor infected by this license in any way.\n")); +} + + +void print_usage(const char *appl, enum log_level level) +{ + const char *applp; + + /* strip pathprefix from appl */ + applp = strrchr(appl, '\\'); + if (applp != NULL) + { + appl = applp + 1; + } /* done */ + applp = strrchr(appl, '/'); + if (applp != NULL) + { + appl = applp + 1; + } + /* done */ + LOG(level, ("usage: %s [option]... infile[,
]...\n", appl)); + LOG(level, + (" -n ,[]\n" + " renumbers the basic program. line numbers will start at \n" + " and be incremented with . can't be combined with -N\n" + " -N crunch optimized renumbering. basic program will be runnable\n" + " but not editable after this. can't be combined with -n\n" + " -p save file with crunch optimized (broken) line links. if\n")); + LOG(level, + (" combined with -t, the trampoline will repair the links before\n" + " it runs the program.\n" + " -r remove remarks and unneccessary spaces\n" + " -t generate c64 trampoline, the trampoline routine will be\n" + " located at the beginning of the outfile and is started by\n" + " jmp:ing to the first address it loads to.\n" + " -c trampoline will also recreate the stack color table, must be\n" + " combined with -t and -4.\n")); + LOG(level, + (" -4 generate c16/plus4 trampoline instead of c64, must be combined\n" + " with -t.\n" + " -- treat all args to the right as non-options\n" + " -? displays this help screen\n" + " -v displays version and the usage license\n" + " The name of the outfile(s) will be the name of the infile(s) with a suffix of\n" + " \".out.prg\" added.\n")); +} + +int main(int argc, char *argv[]) +{ + int renargs[2] = {-1, 5}; + int renflags = 0; + + int patch_links = 0; + int remarks = 0; + int renumber = 0; + int reNumber = 0; + int trampoline = 0; + int c264_color = 0; + int c264 = 0; + int *t_start = NULL; + int *t_var = NULL; + int *t_end = NULL; + + int c, infilec; + char **infilev; + + /* init logging */ + LOG_INIT_CONSOLE(LOG_NORMAL); + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + while ((c = getflag(argc, argv, "s:n:Nprt4cv")) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch (c) + { + case 'r': + LOG(LOG_DEBUG, ("option -r: remove remarks and spaces\n")); + remarks = 1; + break; + case 'p': + LOG(LOG_DEBUG, ("option -p: setting link patch mode\n")); + patch_links = 1; + break; + case 'N': + reNumber = 1; + renflags = 1; + renargs[0] = 0; + renargs[1] = 1; + LOG(LOG_DEBUG, ("option -N: brutal renumber\n")); + break; + case 'n': + if (read_int_sequence(flagarg, 2, renargs) < 0 || + renargs[0] < 0 || renargs[0] >= 63999 || + renargs[1] < 0 || renargs[1] >= 63999) + { + LOG(LOG_ERROR, + ("error: invalid number for -n option, " + "must be in the range of [0 - 63999]\n")); + print_usage(argv[0], LOG_ERROR); + exit(1); + } + LOG(LOG_DEBUG, ("option -n: nice renumber, " + "start with %d, increment %d\n", + renargs[0],renargs[1])); + renumber = 1; + break; + case 't': + LOG(LOG_DEBUG, ("option -t: adding trampoline\n")); + trampoline = 1; + break; + case 'c': + LOG(LOG_DEBUG, ("option -c: adding c264 color table regen.\n")); + c264_color = 1; + break; + case '4': + LOG(LOG_DEBUG, ("option -4: using c264 trampoline\n")); + c264 = 1; + break; + case 'v': + print_license(); + exit(0); + default: + if (flagflag != '?') + { + LOG(LOG_ERROR, + ("error, invalid option \"-%c\"", flagflag)); + if (flagarg != NULL) + { + LOG(LOG_ERROR, (" with argument \"%s\"", flagarg)); + } + LOG(LOG_ERROR, ("\n")); + } + print_usage(argv[0], LOG_WARNING); + exit(0); + } + } +#if 0 + LOG(LOG_DEBUG, ("flagind %d\n", flagind)); + for (c = 0; c < argc; ++c) + { + if (c == flagind) + { + LOG(LOG_DEBUG, ("-----------------------\n")); + } + LOG(LOG_DEBUG, ("argv[%d] = \"%s\"\n", c, argv[c])); + } + exit(1); +#endif + if(renumber && reNumber) + { + LOG(LOG_ERROR, ("error: the -N and -n options can't be combined.\n")); + print_usage(argv[0], LOG_ERROR); + exit(1); + } + if(c264 && !trampoline) + { + LOG(LOG_ERROR, + ("error: the -4 option must be combined with -t.\n")); + print_usage(argv[0], LOG_ERROR); + exit(1); + } + if(c264_color && !c264) + { + LOG(LOG_ERROR, + ("error: the -c option must be combined with -t and -4.\n")); + print_usage(argv[0], LOG_ERROR); + exit(1); + } + + infilev = argv + flagind; + infilec = argc - flagind; + + if(infilec == 0) + { + LOG(LOG_ERROR, + ("error: at least one infile has to be specified.\n")); + print_usage(argv[0], LOG_ERROR); + exit(1); + } + + for(c = 0; c < infilec; ++c) + { + struct bprg_ctx ctx; + char buf[1000]; + + bprg_init(&ctx, infilev[c]); + + if(remarks) + { + LOG(LOG_NORMAL, (" - removing remarks and spaces")); + bprg_rem_remove(&ctx); + LOG(LOG_NORMAL, (", done.\n")); + } + + if(renumber || reNumber) + { + LOG(LOG_NORMAL, (" - renumbering basic lines")); + bprg_renumber(&ctx, renargs[0], renargs[1], renflags); + LOG(LOG_NORMAL, (", done.\n")); + } + + if(trampoline) + { + int flags; + + LOG(LOG_NORMAL, (" - adding trampoline @ %04X", + ctx.start)); + flags = 0; + flags |= patch_links ? TRAMPOLINE_FLAG_REGEN: 0; + flags |= c264 ? TRAMPOLINE_FLAG_C264: 0; + flags |= c264_color ? TRAMPOLINE_FLAG_C264_COLOR_REGEN: 0; + + if(flags & TRAMPOLINE_FLAG_C264) + { + LOG(LOG_NORMAL, (" for c16/plus4")); + } + if(flags & TRAMPOLINE_FLAG_REGEN) + { + LOG(LOG_NORMAL, (" that recreates links")); + } + if(flags & TRAMPOLINE_FLAG_C264_COLOR_REGEN) + { + LOG(LOG_NORMAL, (" and color table")); + } + + bprg_trampoline_add(&ctx, t_start, t_var, t_end, flags); + LOG(LOG_NORMAL, (", done.\n")); + } + if(patch_links) + { + LOG(LOG_NORMAL, (" - clobbering basic line links")); + bprg_link_patch(&ctx); + LOG(LOG_NORMAL, (", done.\n")); + } + + strcpy(buf, infilev[c]); + strcat(buf, ".out.prg"); + + bprg_save(&ctx, buf); + + bprg_free(&ctx); + } + + return 0; +} diff --git a/loader/tools/exomizer-3.1/src/bprg.c b/loader/tools/exomizer-3.1/src/bprg.c new file mode 100644 index 0000000..94f596e --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include "bprg.h" +#include "log.h" + +FILE * +open_file(const char *name, int *load_addr); + +void +bprg_init(struct bprg_ctx *ctx, const char *prg_name) +{ + FILE *in; + int load; + int len; + + ctx->len = 0; + memset(ctx->mem, 0, sizeof(*ctx->mem)); + + in = open_file(prg_name, &load); + if (in == NULL) + { + LOG(LOG_ERROR, + (" can't open file \"%s\" for input\n", prg_name)); + exit(1); + } + + ctx->start = load; + ctx->basic_start = load; + + LOG(LOG_VERBOSE, ("load start $%04X\n", load)); + len = fread(ctx->mem + load, 1, sizeof(ctx->mem) - load, in); + LOG(LOG_VERBOSE, ("load end $%04X\n", load + len)); + + fclose(in); + LOG(LOG_NORMAL, ("read %d bytes (%d blocks) from file " + "\"%s\",$%04X,$%04X.\n", + len, (len + 255) / 254, prg_name, load, load + len)); + + + + /* this call rebuilds broken links */ + bprg_lines_mutate(ctx, NULL, NULL); +} + +void +bprg_save(struct bprg_ctx *ctx, const char *prg_name) +{ + FILE *out; + out = fopen(prg_name,"wb"); + + fputc(ctx->start & 255, out); + fputc(ctx->start >> 8, out); + + fwrite(ctx->mem + ctx->start, 1, ctx->len, out); + + fclose(out); + + LOG(LOG_NORMAL, ("wrote %d bytes (%d blocks) to file " + "\"%s\",$%04X,$%04X.\n", + ctx->len, (ctx->len + 255) / 254, prg_name, + ctx->start, ctx->start + ctx->len)); + +} + +void +bprg_free(struct bprg_ctx *ctx) +{ +} + +void +bprg_get_iterator(struct bprg_ctx *ctx, struct bprg_iterator *i) +{ + i->mem = ctx->mem; + i->pos = ctx->basic_start; +} + +int +bprg_iterator_next(struct bprg_iterator *i, struct brow **b) +{ + if (i->mem[i->pos + 1] == '\0') + { + return 0; + } + if(b != NULL) + { + *b = (struct brow*)(i->mem + i->pos); + } + + /* set pos to next line */ + i->pos = i->mem[i->pos] | (i->mem[i->pos + 1] << 8); + + return 1; +} + +static int clone_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + unsigned short start; + unsigned short i; + + start = *pos; + i = 0; + + /* skip link */ + i += 2; in += 2; + /* copy number */ + mem[start + i++] = *(in++); + mem[start + i++] = *(in++); + /* copy line including terminating '\0' */ + while((mem[start + i++] = *(in++)) != '\0'); + + /* set link properly */ + mem[start] = start + i; + mem[start + 1] = (start + i) >> 8; + + *pos = start + i; + /* success */ + return 0; +} + +void +bprg_lines_mutate(struct bprg_ctx *ctx, + cb_line_mutate *f, + void *priv) +{ + unsigned short pos; + unsigned short len; + static unsigned char mem[65536]; + unsigned char *p; + + memset(mem, 0, sizeof(mem)); + + pos = ctx->start; + p = ctx->mem + pos; + + /* copy trampoline */ + while (pos < ctx->basic_start) + { + mem[pos++] = *(p++); + } + + while (p[1] != '\0') + { + /* call callback function */ + if(f == NULL || f(p, mem, &pos, priv) != 0) + { + /* it was not interested, call plain clone */ + clone_cb_line_mutate(p, mem, &pos, priv); + } + /* skip link and line */ + p += 4; + /* eat line including terminating '\0' */ + while(*(p++) != 0); + } + mem[pos++] = '\0'; + mem[pos++] = '\0'; + len = pos - ctx->start; + + /* copy local stuff to context */ + memcpy(ctx->mem, mem, sizeof(mem)); + ctx->len = len; + + LOG(LOG_DUMP, ("program length %hu\n", ctx->len)); +} diff --git a/loader/tools/exomizer-3.1/src/bprg.h b/loader/tools/exomizer-3.1/src/bprg.h new file mode 100644 index 0000000..24c7d89 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg.h @@ -0,0 +1,101 @@ +#ifndef ALREADY_INCLUDED_BPRG +#define ALREADY_INCLUDED_BPRG +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "vec.h" + +struct bprg_ctx { + unsigned short len; + unsigned short start; + unsigned short basic_start; + unsigned char mem[65536]; +}; + +struct bprg_iterator { + const unsigned char *mem; + unsigned short pos; +}; + +typedef int cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv); /* IN/OUT */ + +void +bprg_init(struct bprg_ctx *ctx, const char *prg_name); + +void +bprg_save(struct bprg_ctx *ctx, const char *prg_name); + +void +bprg_free(struct bprg_ctx *ctx); + +void +bprg_get_iterator(struct bprg_ctx *ctx, struct bprg_iterator *i); + +struct brow { + unsigned char row[1]; /* Flexible array member */ +}; + +int +bprg_iterator_next(struct bprg_iterator *i, struct brow **b); + +void +bprg_lines_mutate(struct bprg_ctx *ctx, + cb_line_mutate *f, + void *priv); + +/* higher level functions */ +void +bprg_renumber(struct bprg_ctx *ctx, + int start, + int step, + int mode); + +void +bprg_link_patch(struct bprg_ctx *ctx); + +void +bprg_rem_remove(struct bprg_ctx *ctx); + +#define TRAMPOLINE_FLAG_C264 1 +#define TRAMPOLINE_FLAG_REGEN 2 +#define TRAMPOLINE_FLAG_C264_COLOR_REGEN 4 + +void +bprg_trampoline_add(struct bprg_ctx *ctx, + int *start, int *var, int *end, + int flags); +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/bprg_actions.h b/loader/tools/exomizer-3.1/src/bprg_actions.h new file mode 100644 index 0000000..c66d3f2 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg_actions.h @@ -0,0 +1,45 @@ +#ifndef ALREADY_INCLUDED_BPRG_ACTIONS +#define ALREADY_INCLUDED_BPRG_ACTIONS +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "bprg.h" + +void +bprg_renumber(struct bprg_ctx *ctx); + +void +bprg_link_patch(struct bprg_ctx *ctx); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/bprg_link_patch.c b/loader/tools/exomizer-3.1/src/bprg_link_patch.c new file mode 100644 index 0000000..609ae74 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg_link_patch.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include "bprg.h" +#include "log.h" + +static int +linkpatch_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + unsigned short start; + unsigned short i; + + start = *pos; + i = 0; + + /* skip link */ + i += 2; in += 2; + /* copy number */ + mem[start + i++] = *(in++); + mem[start + i++] = *(in++); + /* copy line including terminating '\0' */ + while((mem[start + i++] = *(in++)) != '\0'); + + /* the purpose of this whole "if" structure is to improve the + * crunching of the basic program by changing the basic line + * link pointers to something that crunches better. This is + * possible since the basic normally recreates the links when + * loading the basic program anyhow. However if we decrunch + * the basic program this will not happen. But there is still + * a nice subroutine that does the job for us that we can call + * from the basic start trampoline */ + + /* link patching begins here */ + if(mem[start + 2] == '\0' && mem[start + 4] != '\0') + { + /* mask 1, length 3 offset 3*/ + mem[start + 0] = mem[start + 3]; + mem[start + 1] = mem[start + 4]; + + } else if(mem[start + 3] == '\0' && mem[start + 5] != '\0') + { + /* mask 2, length 3 offset 4*/ + mem[start + 0] = mem[start + 4]; + mem[start + 1] = mem[start + 5]; + + } else if(mem[start + 3] != '\0') + { + /* mask 3, length 2 offset 2*/ + mem[start + 0] = mem[start + 2]; + mem[start + 1] = mem[start + 3]; + + } else if(mem[start + 4] != '\0') + { + /* mask 4, length 2 offset 3*/ + mem[start + 0] = mem[start + 3]; + mem[start + 1] = mem[start + 4]; + } else + { + /* if we ever get here I have made a big mistake in + assuming that the test (mem[start + 4] != '\0') always is true + since empty basic lines are forbidden */ + LOG(LOG_ERROR, ("Error, can't patch link (should be impossible)")); + exit(1); + } + /* link patching ends here */ + + *pos = start + i; + /* success */ + return 0; +} + +void +bprg_link_patch(struct bprg_ctx *ctx) +{ + bprg_lines_mutate(ctx, linkpatch_cb_line_mutate, NULL); + LOG(LOG_VERBOSE, ("program length %hu\n", ctx->len)); +} + diff --git a/loader/tools/exomizer-3.1/src/bprg_renumber.c b/loader/tools/exomizer-3.1/src/bprg_renumber.c new file mode 100644 index 0000000..b09828a --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg_renumber.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2003, 2013 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include "bprg.h" +#include "vec.h" +#include "log.h" + +/* data */ +#define TOK_DATA '\x83' + +/* line numbers */ +#define TOK_GOTO '\x89' +#define TOK_RUN '\x8A' +#define TOK_GOSUB '\x8D' +#define TOK_REM '\x8F' +#define TOK_LIST '\x9B' +#define TOK_TO '\xA4' +#define TOK_THEN '\xA7' +#define TOK_GO '\xCB' + +#define TOKSTR_NUMBER "\x89\x8A\x8D\x8F\x9B\xA7\xCB" + +struct goto_fixup { + unsigned char *where; + int old_line; + int from_line; +}; + +struct goto_target { + int old_line; + int new_line; +}; + +struct renumber_ctx { + struct vec fixups; + struct vec targets; + struct vec_iterator i; + struct goto_fixup *fixup; + int line; + int step; + int mode; +}; + +static int goto_target_cb_cmp(const void *a, const void *b) +{ + int val = 0; + struct goto_target *a1 = (struct goto_target*)a; + struct goto_target *b1 = (struct goto_target*)b; + + if (a1->old_line < b1->old_line) + { + val = -1; + } else if (a1->old_line > b1->old_line) + { + val = 1; + } + return val; +} + +static void +goto_fixup_add(struct bprg_ctx *ctx, + struct renumber_ctx *rctx, + int line, + char **p) +{ + struct goto_fixup fixup; + struct goto_target target; + + unsigned short number; + char *n; + char *n1; + + n = *p; + do { + + /* eat spaces */ + while(*n == ' ') ++n; + + n1 = n; + if(*n == '\0') break; + + number = strtol(n, &n1, 10); + + if (n == n1) + { + if(n[-1] != TOK_THEN) + { + /* error, should not happen */ + LOG(LOG_VERBOSE, + ("can't convert $%02X at line %d \"%s\" to line number\n", + *n, line, n)); + } + break; + } + + /* register the goto into the fixup vec */ + fixup.where = (unsigned char *)n; + fixup.old_line = number; + fixup.from_line = line; + + LOG(LOG_VERBOSE, ("adding fixup line %d\n", line)); + vec_push(&rctx->fixups, &fixup); + + target.old_line = number; + target.new_line = -1; + + vec_insert_uniq(&rctx->targets, goto_target_cb_cmp, &target, NULL); + + /* always add a reverse mapping is not optimal + * but an optimal approach would include graph traversal.. */ + target.old_line = line; + target.new_line = -1; + + vec_insert_uniq(&rctx->targets, goto_target_cb_cmp, &target, NULL); + + n = n1; + + /* eat spaces */ + while(*n == ' ') ++n; + + if(*n != ',') break; + + /* *n == ',' here + int max_diff; + int diff; + int traits_used = 0; */ + ++n; + + } while (1); + + *p = n; +} + +static void +goto_scan(struct bprg_ctx *ctx, + struct renumber_ctx *rctx) +{ + struct bprg_iterator i; + struct brow *b; + + bprg_get_iterator(ctx, &i); + + while(bprg_iterator_next(&i, &b)) + { + const char *accept[3] = {TOKSTR_NUMBER "\"", "\"", ""}; + int line; + + char *p; + int quote = 0; + + LOG(LOG_DUMP, + ("scanning line \"%s\"\n", b->row + 4)); + + p = (char*)b->row; + + line = (unsigned char)p[2] | ((unsigned char)p[3] << 8); + + /* skip link and number */ + p += 4; + + while ((p = strpbrk(p, accept[quote])) != NULL) + { + char tok = *(p++); + switch (tok) { + case '\"': + /* toggle quote mode */ + quote ^= 1; + break; + case TOK_REM: + /* skip this line, the rest is an rem statement */ + LOG(LOG_DUMP, ("skipping rem\n")); + quote = 2; + break; + case TOK_GO: + while (*p == ' ') ++p; + if(*p != TOK_TO) + { + LOG(LOG_WARNING, ("found GO without TO, skipping\n")); + break; + } + ++p; + goto_fixup_add(ctx, rctx, line, &p); + break; + case TOK_GOSUB: + LOG(LOG_DUMP, ("found gosub on line %d\n", line)); + default: + /* we found a basic token that uses line numbers */ + goto_fixup_add(ctx, rctx, line, &p); + break; + } + } + } +} + +static int +renumber1_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + struct renumber_ctx *rctx; + struct goto_target target_key; + struct goto_target *target; + int start; + int i; + int line; + + rctx = priv; + + /* prepare target key */ + target_key.old_line = in[2] | (in[3] << 8); + /* update targets vector with the new line number */ + target = vec_find2(&rctx->targets, goto_target_cb_cmp, &target_key); + + line = 0; + if(target != NULL || rctx->mode == 0) + { + line = rctx->line; + } + if(target != NULL) + { + target->new_line = line; + } + + LOG(LOG_DUMP, ("renumbering line %d to %d (target %p)\n", + target_key.old_line, line, (void*)target)); + + if(target != NULL || rctx->mode == 0) + { + /* update line number for next line */ + rctx->line += rctx->step; + } + + start = *pos; + i = 0; + + /* copy link */ + mem[start + i++] = *(in++); + mem[start + i++] = *(in++); + /* skip number */ + i += 2; in += 2; + /* copy line including terminating '\0' */ + while((mem[start + i++] = *in++) != '\0'); + *pos = start + i; + + /* the actual renumbering of this row */ + mem[start + 2] = line; + mem[start + 3] = line >> 8; + + return 0; +} + +static int +renumber2_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + struct renumber_ctx *rctx; + int start; + int i; + unsigned char c; + + rctx = priv; + + + start = *pos; + i = 0; + + /* skip link */ + i += 2; + in += 2; + /* copy number */ + mem[start + i++] = *(in++); + mem[start + i++] = *(in++); + /* copy line including terminating '\0' */ + do { + if (rctx->fixup != NULL && rctx->fixup->where == in) + { + /* hoaa, fixup reference */ + struct goto_target target_key; + struct goto_target *target; + + LOG(LOG_DUMP, ("found fixup goto %u at %p\n", + rctx->fixup->old_line, (void*)in)); + + target_key.old_line = rctx->fixup->old_line; + target = vec_find2(&rctx->targets, + goto_target_cb_cmp, + &target_key); + if(target == NULL) + { + LOG(LOG_ERROR, ("found fixup has no target \n")); + exit(1); + } + if(target->new_line == -1) + { + LOG(LOG_WARNING, + ("warning at line %d: nonexisting line %d " + "is renumbered to %d\n", + rctx->fixup->from_line, + target->old_line, + target->new_line)); + } + + /* write new line number */ + i += sprintf((char*)mem + start + i, "%d", target->new_line); + /* skip old in input */ + strtol((char*)in, (void*)&in, 10); + + /* set where to next fixup */ + rctx->fixup = vec_iterator_next(&rctx->i); + } + /* copy byte normally */ + c = *(in++); + mem[start + i++] = c; + } while(c != '\0'); + + /* set link properly */ + mem[start] = start + i; + mem[start + 1] = (start + i) >> 8; + + *pos = start + i; + /* success */ + return 0; +} + +void +bprg_renumber(struct bprg_ctx *ctx, + int start, + int step, + int mode) +{ + struct renumber_ctx rctx; + + vec_init(&rctx.fixups, sizeof(struct goto_fixup)); + vec_init(&rctx.targets, sizeof(struct goto_target)); + + goto_scan(ctx, &rctx); + + rctx.line = start; + rctx.step = step; + rctx.mode = mode; + + /* just renumber the lines */ + bprg_lines_mutate(ctx, renumber1_cb_line_mutate, &rctx); + + vec_get_iterator(&rctx.fixups, &rctx.i); + rctx.fixup = vec_iterator_next(&rctx.i); + + /* now fixup line references */ + bprg_lines_mutate(ctx, renumber2_cb_line_mutate, &rctx); + + vec_free(&rctx.targets, NULL); + vec_free(&rctx.targets, NULL); +} + +static int +rem_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + int start; + int i; + char c; + int quote; + int data; + int line; + + struct renumber_ctx *rctx; + rctx = priv; + + start = *pos; + i = 0; + + /* skip link */ + i += 2; + in += 2; + /* copy number */ + line = *(in++); + line |= (*(in++) << 8); + mem[start + i++] = line; + mem[start + i++] = (line >> 8); + + /* copy line including terminating '\0' */ + quote = 0; + data = 0; + do { + c = *(in++); + switch(c) { + case ' ': + if(quote) break; + + /* in data statements we can only ignore unquoted spaces + * if they come immediately after a comma or the DATA token*/ + if(data && + mem[start + i - 1] != ',' && + (char)mem[start + i - 1] != TOK_DATA) + { + break; + } + + /* skip this byte */ + continue; + break; + case ':': + if(quote) break; + data = 0; + break; + case '\"': + quote ^= 1; + break; + case TOK_DATA: + data = 1; + break; + case TOK_REM: + if(quote) break; + if(i == 4) + { + /* rem at the beginning of a line */ + struct goto_target target_key; + struct goto_target *target; + target_key.old_line = line; + target = vec_find2(&rctx->targets, + goto_target_cb_cmp, + &target_key); + if(target == NULL) + { + /* skip this line entirely */ + return 0; + } + /* line is a got target, we can't remove it but + * we can remove the rem text */ + mem[start + i++] = c; + + } else if(i > 4 && mem[start + i - 1] == ':') + { + --i; + } + c = '\0'; + break; + } + mem[start + i++] = c; + + } while(c != '\0'); + + /* set link properly */ + mem[start] = start + i; + mem[start + 1] = (start + i) >> 8; + + *pos = start + i; + /* success */ + return 0; +} + +void +bprg_rem_remove(struct bprg_ctx *ctx) +{ + struct renumber_ctx rctx; + + vec_init(&rctx.fixups, sizeof(struct goto_fixup)); + vec_init(&rctx.targets, sizeof(struct goto_target)); + + goto_scan(ctx, &rctx); + + bprg_lines_mutate(ctx, rem_cb_line_mutate, &rctx); + + vec_free(&rctx.targets, NULL); + vec_free(&rctx.fixups, NULL); +} diff --git a/loader/tools/exomizer-3.1/src/bprg_trampoline.c b/loader/tools/exomizer-3.1/src/bprg_trampoline.c new file mode 100644 index 0000000..4c13665 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/bprg_trampoline.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "bprg.h" +#include "log.h" + +#include + +struct trampoline { + int init; + int start; + int size; +}; + +static int +trampoline_cb_line_mutate(const unsigned char *in, /* IN */ + unsigned char *mem, /* IN/OUT */ + unsigned short *pos, /* IN/OUT */ + void *priv) /* IN/OUT */ +{ + unsigned short start; + unsigned short i; + + struct trampoline *t; + + t = priv; + if(!t->init) + { + /* move pointer ahead */ + *pos = t->start + t->size; + t->init = 1; + } + start = *pos; + i = 0; + + /* skip link */ + i += 2; + in += 2; + /* copy number */ + mem[start + i++] = *(in++); + mem[start + i++] = *(in++); + /* copy line including terminating '\0' */ + while((mem[start + i++] = *(in++)) != '\0'); + + /* set link properly */ + mem[start] = start + i; + mem[start + 1] = (start + i) >> 8; + + *pos = start + i; + /* success */ + return 0; +} + +void +bprg_trampoline_add(struct bprg_ctx *ctx, + int *start, int *var, int *end, + int flags) +{ + struct trampoline t; + int tmp; + int endpos; + int i; + + if(start == NULL) + { + tmp = ctx->basic_start; + start = &tmp; + } + + t.init = 0; + t.start = *start; + + /* start */ + t.size = 8; + + /* var */ + t.size += 8; + + if(end != NULL) + { + t.size += 8; + } + t.size += 7; + if(flags & TRAMPOLINE_FLAG_REGEN) + { + t.size += 3; + } + if(flags & TRAMPOLINE_FLAG_C264_COLOR_REGEN) + { + t.size += 3; + } + bprg_lines_mutate(ctx, trampoline_cb_line_mutate, &t); + + endpos = ctx->start + ctx->len; + if(var != NULL && endpos > *var) + { + LOG(LOG_ERROR, ("error, basic ends at $%04X, " + "must be >= basic vars at $%04X\n", + endpos, + *var)); + exit(1); + } + ctx->start = *start; + ctx->basic_start = ctx->start + t.size; + ctx->len = endpos - ctx->start; + + i = ctx->start; + /* lda #mem[i++] = 0xA9; + ctx->mem[i++] = ctx->basic_start & 0xFF; + /* sta $2B */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x2B; + /* lda #mem[i++] = 0xA9; + ctx->mem[i++] = ctx->basic_start >> 8; + /* sta $2C */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x2C; + + if(var != NULL) + { + endpos = *var; + } + /* lda #mem[i++] = 0xA9; + ctx->mem[i++] = endpos & 0xFF; + /* sta $2D */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x2D; + /* lda #mem[i++] = 0xA9; + ctx->mem[i++] = endpos >> 8; + /* sta $2E */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x2E; + + if(end != NULL) + { + /* lda #mem[i++] = 0xA9; + ctx->mem[i++] = *end & 0xFF; + /* sta $37 */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x37; + /* lda #>end */ + ctx->mem[i++] = 0xA9; + ctx->mem[i++] = *end >> 8; + /* sta $38 */ + ctx->mem[i++] = 0x85; + ctx->mem[i++] = 0x38; + } + if(flags & TRAMPOLINE_FLAG_C264) + { + /* jsr $8BBE */ + ctx->mem[i++] = 0x20; + ctx->mem[i++] = 0xBE; + ctx->mem[i++] = 0x8B; + + if(flags & TRAMPOLINE_FLAG_REGEN) + { + /* jsr $8818 */ + ctx->mem[i++] = 0x20; + ctx->mem[i++] = 0x18; + ctx->mem[i++] = 0x88; + } + + if(flags & TRAMPOLINE_FLAG_C264_COLOR_REGEN) + { + /* jsr $F3B5 */ + ctx->mem[i++] = 0x20; + ctx->mem[i++] = 0xB5; + ctx->mem[i++] = 0xF3; + } + + /* jmp $8BDC */ + ctx->mem[i++] = 0x4C; + ctx->mem[i++] = 0xDC; + ctx->mem[i++] = 0x8B; + } else /* c64 trampoline */ + { + /* jsr $A659 */ + ctx->mem[i++] = 0x20; + ctx->mem[i++] = 0x59; + ctx->mem[i++] = 0xA6; + + if(flags & TRAMPOLINE_FLAG_REGEN) + { + /* jsr $A533 */ + ctx->mem[i++] = 0x20; + ctx->mem[i++] = 0x33; + ctx->mem[i++] = 0xA5; + } + /* jmp $A7AE */ + ctx->mem[i++] = 0x4C; + ctx->mem[i++] = 0xAE; + ctx->mem[i++] = 0xA7; + } + ctx->mem[i++] = 0x00; +} diff --git a/loader/tools/exomizer-3.1/src/buf.c b/loader/tools/exomizer-3.1/src/buf.c new file mode 100644 index 0000000..12f1cdc --- /dev/null +++ b/loader/tools/exomizer-3.1/src/buf.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2002 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include +#include "buf.h" + +void buf_init(struct buf *b) +{ + b->data = NULL; + b->size = 0; + b->capacity = 0; +} + +void buf_free(struct buf *b) +{ + if (b->capacity == -1) + { + fprintf(stderr, "error, can't free a buf view\n"); + exit(1); + } + if (b->data != NULL) + { + free(b->data); + b->data = NULL; + } + b->size = 0; + b->capacity = 0; +} + +void buf_new(struct buf **bp) +{ + struct buf *b; + + b = malloc(sizeof(struct buf)); + if (b == NULL) + { + fprintf(stderr, "error, can't allocate memory\n"); + exit(1); + } + b->data = NULL; + b->size = 0; + b->capacity = 0; + + *bp = b; +} + +void buf_delete(struct buf **bp) +{ + struct buf *b; + + b = *bp; + buf_free(b); + free(b); + b = NULL; + *bp = b; +} + +int buf_size(const struct buf *b) +{ + return b->size; +} + +int buf_capacity(const struct buf *b) +{ + return b->capacity; +} + +void *buf_data(const struct buf *b) +{ + return b->data; +} + +void *buf_reserve(struct buf *b, int new_capacity) +{ + int capacity = b->capacity; + if (capacity == -1) + { + fprintf(stderr, "error, can't reserve capacity for a buf view\n"); + exit(1); + } + if (capacity == 0) + { + capacity = 1; + } + while (capacity < new_capacity) + { + capacity <<= 1; + } + if (capacity > b->capacity) + { + b->data = realloc(b->data, capacity); + if (b->data == NULL) + { + fprintf(stderr, "error, can't reallocate memory\n"); + exit(1); + } + b->capacity = capacity; + } + return b->data; +} + +void buf_clear(struct buf *b) +{ + buf_replace(b, 0, b->size, NULL, 0); +} + +void buf_remove(struct buf *b, int b_off, int b_n) +{ + buf_replace(b, b_off, b_n, NULL, 0); +} + +void *buf_insert(struct buf *b, int b_off, const void *m, int m_n) +{ + return buf_replace(b, b_off, 0, m, m_n); +} + +void *buf_append(struct buf *b, const void *m, int m_n) +{ + return buf_replace(b, b->size, 0, m, m_n); +} + +void buf_append_char(struct buf *b, char c) +{ + buf_replace(b, b->size, 0, &c, 1); +} + +void buf_append_str(struct buf *b, const char *str) +{ + buf_replace(b, b->size, 0, str, strlen(str)); +} + +void *buf_replace(struct buf *b, int b_off, int b_n, const void *m, int m_n) +{ + int new_size; + int rest_off; + int rest_n; + if (b->capacity == -1) + { + fprintf(stderr, "error, can't modify a buf view\n"); + exit(1); + } + if (b_off < 0) + { + b_off += b->size + 1; + } + if (b_n == -1) + { + b_n = b->size - b_off; + } + if(b_off < 0 || b_off > b->size) + { + fprintf(stderr, "error, b_off %d must be within [0 - %d].\n", + b_off, b->size); + exit(1); + } + if(b_n < 0 || b_n > b->size - b_off) + { + fprintf(stderr, + "error, b_n %d must be within [0 and %d] for b_off %d.\n", + b_n, b->size - b_off, b_off); + exit(1); + } + if (m_n < 0) + { + fprintf(stderr, "error, m_n %d must be >= 0.\n", m_n); + exit(1); + } + new_size = b->size - b_n + m_n; + if (new_size > b->capacity) + { + buf_reserve(b, new_size); + } + + rest_off = b_off + b_n; + rest_n = b->size - rest_off; + if (rest_n > 0) + { + memmove((char*)b->data + b_off + m_n, + (char*)b->data + rest_off, rest_n); + } + if (m_n > 0) + { + if (m != NULL) + { + memcpy((char*)b->data + b_off, m, m_n); + } + } + b->size = new_size; + return (char*)b->data + b_off; +} + +static void fskip(FILE *f, int off) +{ + int skip, remaining; + for (remaining = off; remaining > 0; remaining -= skip) + { + char buf[2048]; + if (remaining == 0) + { + /* done */ + break; + } + skip = remaining < 2048 ? remaining : 2048; + if (fread(buf, 1, skip, f) != skip) + { + if (feof(f)) + { + fprintf(stderr, + "Error: EOF occured while skipping %d bytes.\n", + off); + } + else + { + fprintf(stderr, + "Error: Error occured while skipping %d bytes.\n", + off); + } + exit(-1); + } + } +} + +void *buf_freplace(struct buf *b, int b_off, int b_n, + FILE *f, int f_off, int f_n) +{ + char buf[2048]; + int read; + int len; + int pos; + + if (f == NULL) + { + fprintf(stderr, "Error: f must not be NULL.\n"); + exit(-1); + } + if (b->capacity == -1) + { + fprintf(stderr, "error, can't modify a buf view\n"); + exit(1); + } + if (f_off < 0) + { + int f_size; + if(fseek(f, 0, SEEK_END)) + { + fprintf(stderr, "Error: can't seek to EOF in file.\n"); + exit(-1); + } + f_size = ftell(f); + if(fseek(f, 0, SEEK_SET)) + { + fprintf(stderr, "Error: can't seek to start in file.\n"); + exit(-1); + } + if (f_off < 0) + { + f_off += f_size + 1; + } + if (f_n == -1) + { + f_n = f_size - f_off; + } + if(f_off < 0 || f_off > f_size) + { + fprintf(stderr, "Error, f_off %d must be within [0 - %d].\n", + f_off, f_size); + exit(1); + } + if (f_n < 0 || f_n > f_size - f_off) + { + fprintf(stderr, + "Error, f_n %d must be within [0 and %d] for f_off %d.\n", + f_n, f_size - f_off, f_off); + exit(1); + } + } + + /* skip to f_off */ + fskip(f, f_off); + if (ferror(f)) + { + /* An error occured. */ + fprintf(stderr, "Error, failed to skip %d bytes in file.\n", f_off); + exit(-1); + } + + len = (f_n == -1 || f_n > 2048) ? 2048 : f_n; + pos = b_off; + while ((read = fread(buf, 1, len, f)) == len) + { + buf_replace(b, pos, b_n, buf, read); + b_n = 0; + pos += len; + if (f_n != -1) + { + f_n -= len; + len = f_n > 2048 ? 2048 : f_n; + } + } + if (ferror(f)) + { + /* A read error occured. */ + fprintf(stderr, "Error, failed to read from file.\n"); + } + if (read > 0) + { + buf_replace(b, pos, b_n, buf, read); + } + return (char*)b->data + pos; +} + +const struct buf *buf_view(struct buf *v, + const struct buf *b, int b_off, int b_n) +{ + if (b_off < 0) + { + b_off += b->size + 1; + } + if (b_n == -1) + { + b_n = b->size - b_off; + } + if(b_off < 0 || b_off > b->size) + { + fprintf(stderr, "error, b_off %d must be within [0 - %d].\n", + b_off, b->size); + exit(1); + } + if(b_n < 0 || b_n > b->size - b_off) + { + fprintf(stderr, "error, b_n %d must be within [0 - %d].\n", + b_n, b->size - b_off); + exit(1); + } + if (b->data != NULL) + { + v->data = (char*)b->data + b_off; + } + else + { + v->data = NULL; + } + v->size = b_n; + v->capacity = -1; + + return v; +} + +void buf_printf(struct buf *b, const char *format, ...) +{ + int pos; + int printed; + va_list args; + + if (b->capacity == -1) + { + fprintf(stderr, "error, can't printf to a buf view\n"); + exit(1); + } + + pos = b->size; + + va_start(args, format); + printed = vsnprintf((char*)buf_data(b) + pos, b->capacity - pos, + format, args); + va_end(args); + + if (printed >= b->capacity - pos) + { + va_list args2; + + buf_reserve(b, pos + printed + 1); + + va_start(args2, format); + printed = vsnprintf((char*)buf_data(b) + pos, b->capacity - pos, + format, args2); + va_end(args2); + } + b->size += printed; +} diff --git a/loader/tools/exomizer-3.1/src/buf.h b/loader/tools/exomizer-3.1/src/buf.h new file mode 100644 index 0000000..0251f3e --- /dev/null +++ b/loader/tools/exomizer-3.1/src/buf.h @@ -0,0 +1,148 @@ +#ifndef ALREADY_INCLUDED_BUF +#define ALREADY_INCLUDED_BUF +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2016 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ +#include +#ifndef __GNUC__ +#define __attribute__(x) /*NOTHING*/ +#endif + +#define STATIC_BUF_INIT {0, 0, 0} + +struct buf { + void *data; + int size; + int capacity; +}; + +void buf_init(struct buf *b); +void buf_free(struct buf *b); + +void buf_new(struct buf **bp); +void buf_delete(struct buf **bp); + +/* Gets the number of bytes currently in the buffer. Won't change the + * buffer content or size. Won't invalidate previously fetched + * internal buffer pointers. */ +int buf_size(const struct buf *b); + +/* Gets the current capacity of the buf. Won't change the buffer + * content or size. Won't invalidate previously fetched internal + * buffer pointers. */ +int buf_capacity(const struct buf *b); + +/* Gets a pointer to the internal buffer. Don't dereferece it beyond + * the size of the buffer. The returned pointer might be invalidated + * by any future modifying operation. Might return NULL for a just + * initiated buffer. */ +void *buf_data(const struct buf *b); + +/* Ensures that at least the requested capacity is available. Won't + * change the buffer content or size but might invalidate previously + * fetched internal buffer pointers. returns a pointer to the internal + * buffer like a buf_data call. */ +void *buf_reserve(struct buf *b, int capacity); + +/* Clears the given buffer. This will set its size to zero. Won't + * invalidate previously fetched internal buffer pointers. */ +void buf_clear(struct buf *b); + +/* Removes b_n number of bytes at offset b_off from the given + * buffer. A value of -1 for b_n is allowed and interpreted as "to the + * end of the buffer". Won't invalidate previously fetched internal + * buffer pointers. Returns a pointer to the internal buffer like a + * buf_data call. */ +void buf_remove(struct buf *b, int b_off, int b_n); + +/* Inserts b_n number of bytes, copied from mem, at offset b_off in + * the given buffer. Negative values for b_off are allowed and + * interpreted as size + b_off + 1. A value of NULL for m is allowed + * and will leave the target buffer area uninitialized. Might + * invalidate previously fetched internal buffer pointers. Returns a + * pointer to the inserted area in the internal buffer. */ +void *buf_insert(struct buf *b, int b_off, const void *m, int m_n); + +/* Appends to the end of the buffer. Returns a pointer to the internal + * buffer like a buf_data call. A value of NULL for m is allowed and + * will leave the target buffer area uninitialized. Might invalidate + * previously fetched internal buffer pointers. Returns a pointer to + * the appended area in the internal buffer.*/ +void *buf_append(struct buf *b, const void *m, int m_n); + +/* Appends a char to the end of the buffer. Might invalidate + * previously fetched internal buffer pointers. */ +void buf_append_char(struct buf *b, char c); + +/* Appends a zero terminated string to the end of the buffer excluding + * the zero terminator. Might invalidate previously fetched internal + * buffer pointers. */ +void buf_append_str(struct buf *b, const char *str); + +/* Performs inserts, deletes, appends, prepends and replacements. + * Negative values for b_off are allowed and interpreted as size + + * b_off + 1. A value of -1 for b_n is allowed and interpreted as size + * - b_off. A value of NULL for m is allowed and will leave the target + * buffer area uninitialized. Might invalidate previously fetched + * internal buffer pointers. Returns a pointer to the replaced area in + * the internal buffer. */ +void *buf_replace(struct buf *b, int b_off, int b_n, const void *m, int m_n); + +/* Performs inserts, deletes, appends, prepends and replacements from + * the given file f. A negative value for b_off is allowed and + * interpreted as size + b_off + 1. A value of -1 for b_n is allowed + * and interpreted as size - b_off. A negative value for f_off is + * allowed and interpreted as the file size + b_off + 1. A value of -1 + * for f_n is allowed and interpreted as the file size - f_off. Might + * invalidate previously fetched internal buffer pointers. Returns a + * pointer to the replaces area in the internal buffer like a buf_data + * call. */ +void *buf_freplace(struct buf *b, int b_off, int b_n, + FILE *f, int f_off, int f_n); + +/** + * Creates a read only view into another buf starting at the given + * offset and being of the given size. The view buf will not take + * ownership of the data and is not to be freed. + */ +const struct buf *buf_view(struct buf *v, + const struct buf *b, int b_off, int b_n); + +/* Prints formatted to the end of the buffer using the vsnsprintf + * function. Will place a zero byte string terminator in the + * position directly following the end of the buffer to make it + * printf-able. */ +void buf_printf(struct buf *b, const char *format, ...) + __attribute__((format(printf,2,3))); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/buf_io.c b/loader/tools/exomizer-3.1/src/buf_io.c new file mode 100644 index 0000000..ac07e3b --- /dev/null +++ b/loader/tools/exomizer-3.1/src/buf_io.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "buf_io.h" +#include "log.h" +#include +#include + +void read_file(const char *name, struct buf *buf) +{ + char block[1024]; + FILE *in; + int len; + + in = fopen(name, "rb"); + if(in == NULL) + { + LOG(LOG_ERROR, ("Can't open file \"%s\" for input.\n", name)); + exit(1); + } + do + { + len = fread(block, 1, 1024, in); + buf_append(buf, block, len); + } + while(len == 1024); + LOG(LOG_DEBUG, ("read %d bytes from file\n", len)); + fclose(in); +} + +void write_file(const char *name, struct buf *buf) +{ + FILE *out; + out = fopen(name, "wb"); + if(out == NULL) + { + LOG(LOG_ERROR, ("Can't open file \"%s\" for output.\n", name)); + exit(1); + } + fwrite(buf_data(buf), 1, buf_size(buf), out); + fclose(out); +} diff --git a/loader/tools/exomizer-3.1/src/buf_io.h b/loader/tools/exomizer-3.1/src/buf_io.h new file mode 100644 index 0000000..2e409a5 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/buf_io.h @@ -0,0 +1,41 @@ +#ifndef BUF_IO_ALREADY_INCLUDED +#define BUF_IO_ALREADY_INCLUDED +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "buf.h" +void read_file(const char *name, struct buf *buf); +void write_file(const char *name, struct buf *buf); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/callback.h b/loader/tools/exomizer-3.1/src/callback.h new file mode 100644 index 0000000..57348ec --- /dev/null +++ b/loader/tools/exomizer-3.1/src/callback.h @@ -0,0 +1,43 @@ +#ifndef ALREADY_INCLUDED_CALLBACK +#define ALREADY_INCLUDED_CALLBACK +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include + +typedef int cb_cmp(const void *a, const void *b); +typedef void cb_free(void *a); +typedef void cb_fprint(FILE *f, const void *a); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/chunkpool.c b/loader/tools/exomizer-3.1/src/chunkpool.c new file mode 100644 index 0000000..ab64b91 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/chunkpool.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2003 - 2005, 2015 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "chunkpool.h" +#include "log.h" +#include +#include + +void +chunkpool_init(struct chunkpool *ctx, int item_size) +{ + ctx->item_size = item_size; + ctx->item_end = (0x1fffff / item_size) * item_size; + ctx->item_pos = ctx->item_end; + ctx->current_chunk = NULL; + vec_init(&ctx->used_chunks, sizeof(void*)); + ctx->alloc_count = 0; +} + +static void chunk_free(void *chunks, int item_pos, int item_size, cb_free *f) +{ + if (chunks != NULL && f != NULL) + { + do + { + item_pos -= item_size; + f((char*)chunks + item_pos); + } + while(item_pos > 0); + } +} + +void +chunkpool_free2(struct chunkpool *ctx, cb_free *f) +{ + void **chunkp; + struct vec_iterator i; + if (ctx->current_chunk != NULL) + { + chunk_free(ctx->current_chunk, ctx->item_pos, ctx->item_size, f); + free(ctx->current_chunk); + } + vec_get_iterator(&ctx->used_chunks, &i); + while ((chunkp = vec_iterator_next(&i)) != NULL) + { + chunk_free(*chunkp, ctx->item_end, ctx->item_size, f); + free(*chunkp); + } + + ctx->item_size = -1; + ctx->item_end = -1; + ctx->item_pos = -1; + ctx->current_chunk = NULL; + vec_free(&ctx->used_chunks, NULL); +} + +void +chunkpool_free(struct chunkpool *ctx) +{ + chunkpool_free2(ctx, NULL); +} + +void * +chunkpool_malloc(struct chunkpool *ctx) +{ + void *p; + if(ctx->item_pos == ctx->item_end) + { + void *m; + m = malloc(ctx->item_end); + LOG(LOG_DEBUG, ("allocating new chunk %p\n", m)); + if (m == NULL) + { + LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n", + __FILE__, __LINE__)); + LOG(LOG_ERROR, ("alloced %d items of size %d\n", + ctx->alloc_count, ctx->item_size)); + exit(1); + } + vec_push(&ctx->used_chunks, &ctx->current_chunk); + ctx->current_chunk = m; + ctx->item_pos = 0; + } + p = (char*)ctx->current_chunk + ctx->item_pos; + ctx->item_pos += ctx->item_size; + ++ctx->alloc_count; + return p; +} + +void * +chunkpool_calloc(struct chunkpool *ctx) +{ + void *p = chunkpool_malloc(ctx); + memset(p, 0, ctx->item_size); + return p; +} diff --git a/loader/tools/exomizer-3.1/src/chunkpool.h b/loader/tools/exomizer-3.1/src/chunkpool.h new file mode 100644 index 0000000..5c995b9 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/chunkpool.h @@ -0,0 +1,64 @@ +#ifndef ALREADY_INCLUDED_CHUNKPOOL +#define ALREADY_INCLUDED_CHUNKPOOL +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2003 - 2005, 2015 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "callback.h" +#include "vec.h" + +struct chunkpool { + int item_size; + int item_pos; + int item_end; + void *current_chunk; + struct vec used_chunks; + int alloc_count; +}; + +void +chunkpool_init(struct chunkpool *ctx, int item_size); + +void +chunkpool_free(struct chunkpool *ctx); + +void +chunkpool_free2(struct chunkpool *ctx, cb_free *f); + +void * +chunkpool_malloc(struct chunkpool *ctx); + +void * +chunkpool_calloc(struct chunkpool *ctx); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/common.h b/loader/tools/exomizer-3.1/src/common.h new file mode 100644 index 0000000..69a5679 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/common.h @@ -0,0 +1,40 @@ +#ifndef ALREADY_INCLUDED_CB +#define ALREADY_INCLUDED_CB +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +typedef int cb_cmp(const void *a, const void *b); +typedef void cb_free(void *a); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/desfx.c b/loader/tools/exomizer-3.1/src/desfx.c new file mode 100644 index 0000000..6fdb501 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/desfx.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2007 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "desfx.h" +#include "6502emu.h" +#include "log.h" +#include "vec.h" +#include "areatrace.h" + +struct mem_ctx +{ + u8 *mem; + struct areatrace at; +}; + +static void mem_access_write(struct mem_access *this, u16 address, u8 value) +{ + struct mem_ctx *ctx = this->ctx; + + if ((ctx->mem[1] & 4) == 4 && (ctx->mem[1] & 3) != 0 && + address >= 0xd000 && address < 0xe000) + { + /* IO-area written and visible */ + return; + } + + ctx->mem[address] = value; + areatrace_access(&ctx->at, address); +} + +static u8 mem_access_read(struct mem_access *this, u16 address) +{ + struct mem_ctx *ctx = this->ctx; + return ctx->mem[address]; +} + +u16 decrunch_sfx(u8 mem[65536], u16 run, int *startp, int *endp, u32 *cyclesp) +{ + struct mem_ctx m; + struct cpu_ctx r; + r.cycles = 0; + r.mem.ctx = &m; + r.mem.read = mem_access_read; + r.mem.write = mem_access_write; + r.pc = run; + r.sp = '\xf6'; + r.flags = 0; + + m.mem = mem; + m.mem[1] = 0x37; + areatrace_init(&m.at); + + LOG(LOG_DEBUG, ("run %04x\n", run)); + + /* setting up decrunch */ + while(r.pc >= 0x0400 || r.sp != 0xf6) + { + next_inst(&r); + } + + /* decrunching */ + while(r.pc < 0x400) + { + next_inst(&r); + } + + areatrace_merge_overlapping(&m.at); + + areatrace_get_largest(&m.at, startp, endp); + if(cyclesp != NULL) + { + *cyclesp = r.cycles; + } + + areatrace_free(&m.at); + + return r.pc; +} diff --git a/loader/tools/exomizer-3.1/src/desfx.h b/loader/tools/exomizer-3.1/src/desfx.h new file mode 100644 index 0000000..7a4e933 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/desfx.h @@ -0,0 +1,41 @@ +#ifndef ALREADY_INCLUDED_DESFX +#define ALREADY_INCLUDED_DESFX +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2007 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "int.h" + +u16 decrunch_sfx(u8 mem[65536], u16 run, int *start, int *end, u32 *cycles); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/exo_helper.c b/loader/tools/exomizer-3.1/src/exo_helper.c new file mode 100644 index 0000000..586720c --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_helper.c @@ -0,0 +1,858 @@ +/* + * Copyright (c) 2005, 2013, 2015 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "log.h" +#include "output.h" +#include "buf.h" +#include "buf_io.h" +#include "match.h" +#include "search.h" +#include "optimal.h" +#include "exodec.h" +#include "exo_helper.h" +#include "exo_util.h" +#include "getflag.h" +#include +#include +#include + +static const struct crunch_options default_options = CRUNCH_OPTIONS_DEFAULT; + +void do_output_backwards(struct match_ctx *ctx, + struct search_node *snp, + struct encode_match_data *emd, + const struct crunch_options *options, + struct buf *outbuf, + struct crunch_info *infop) +{ + int pos; + int pos_diff; + int max_diff; + int diff; + int traits_used = 0; + int max_len = 0; + int max_offset = 0; + struct output_ctx *old; + struct output_ctx out; + struct search_node *initial_snp; + int initial_len; + int alignment = 0; + int measure_alignment; + int len0123skip = 3; + + old = emd->out; + emd->out = &out; + + initial_len = buf_size(outbuf); + initial_snp = snp; + measure_alignment = options->flags_proto & PFLAG_BITS_ALIGN_START; + if (options->flags_proto & PFLAG_4_OFFSET_TABLES) + { + len0123skip = 4; + } + for (;;) + { + buf_remove(outbuf, initial_len, -1); + snp = initial_snp; + output_ctx_init(&out, options->flags_proto, outbuf); + + output_bits(&out, alignment, 0); + + pos = output_get_pos(&out); + + pos_diff = pos; + max_diff = 0; + + if (snp != NULL) + { + LOG(LOG_DUMP, ("pos $%04X\n", out.pos)); + + output_gamma_code(&out, 16); + output_bits(&out, 1, 0); /* 1 bit out */ + + diff = output_get_pos(&out) - pos_diff; + if(diff > max_diff) + { + max_diff = diff; + } + + LOG(LOG_DUMP, ("pos $%04X\n", out.pos)); + LOG(LOG_DUMP, ("------------\n")); + } + while (snp != NULL) + { + const struct match *mp; + + mp = &snp->match; + if (mp != NULL && mp->len > 0) + { + if (mp->offset == 0) + { + int splitLitSeq = + snp->prev->match.len == 0 && + (options->flags_proto & PFLAG_IMPL_1LITERAL); + int i = 0; + if (mp->len > 1) + { + int len = mp->len; + if (splitLitSeq) + { + --len; + } + for(; i < len; ++i) + { + output_byte(&out, ctx->buf[snp->index + i]); + } + output_bits(&out, 16, len); + output_gamma_code(&out, 17); + output_bits(&out, 1, 0); + /* literal sequence */ + LOG(LOG_DUMP, ("[%d] literal copy len %d\n", out.pos, + len)); + traits_used |= TFLAG_LIT_SEQ; + if (len > max_len) + { + max_len = len; + } + } + if (i < mp->len) + { + /* literal */ + LOG(LOG_DUMP, ("[%d] literal $%02X\n", out.pos, + ctx->buf[snp->index + i])); + output_byte(&out, ctx->buf[snp->index + i]); + if (!splitLitSeq) + { + output_bits(&out, 1, 1); + } + } + } else + { + unsigned int latest_offset = snp->prev->latest_offset; + if (latest_offset > 0) + { + LOG(LOG_DUMP, + ("[%d] offset reuse bit = %d, latest = %d\n", + out.pos, mp->offset == latest_offset, + latest_offset)); + } + LOG(LOG_DUMP, ("[%d] sequence offset = %d, len = %d\n", + out.pos, mp->offset, mp->len)); + optimal_encode(mp, emd, latest_offset, NULL); + output_bits(&out, 1, 0); + if (mp->len == 1) + { + traits_used |= TFLAG_LEN1_SEQ; + } + else + { + int lo = mp->len & 255; + int hi = mp->len & ~255; + if (hi > 0 && lo < len0123skip) + { + traits_used |= TFLAG_LEN0123_SEQ_MIRRORS; + } + } + if (mp->offset > max_offset) + { + max_offset = mp->offset; + } + if (mp->len > max_len) + { + max_len = mp->len; + } + } + + pos_diff += mp->len; + diff = output_get_pos(&out) - pos_diff; + if(diff > max_diff) + { + max_diff = diff; + } + } + LOG(LOG_DUMP, ("------------\n")); + snp = snp->prev; + } + + LOG(LOG_DUMP, ("pos $%04X\n", out.pos)); + if (options->output_header) + { + /* output header here */ + optimal_out(&out, emd); + LOG(LOG_DUMP, ("pos $%04X\n", out.pos)); + } + + if (!measure_alignment) + { + break; + } + alignment = output_bits_alignment(&out); + measure_alignment = 0; + } + output_bits_flush(&out, !(options->flags_proto & PFLAG_BITS_ALIGN_START)); + + emd->out = old; + + if(infop != NULL) + { + infop->traits_used = traits_used; + infop->max_len = max_len; + infop->max_offset = max_offset; + infop->needed_safety_offset = max_diff; + } +} + +/** + * Reads an exported encoding into the given enc_buf for access in + * forward direction. If read from file the file is read forward but + * reversed if needs_reversing != 0. + */ +static void read_encoding_to_buf(const char *imported_enc, + int flags_proto, + int needs_reversing, + struct buf *enc_buf) +{ + if (imported_enc[0] == '@') + { + ++imported_enc; + read_file(imported_enc, enc_buf); + } + else + { + struct encode_match_data emd; + struct crunch_options options = default_options; + + options.flags_proto = flags_proto; + options.output_header = 1; + options.imported_encoding = imported_enc; + emd.out = NULL; + + optimal_init(&emd, options.flags_notrait, options.flags_proto); + optimal_encoding_import(&emd, options.imported_encoding); + do_output_backwards(NULL, NULL, &emd, &options, enc_buf, NULL); + /* enc_buf is in backward direction */ + optimal_free(&emd); + needs_reversing = 1; + } + + if (needs_reversing) + { + reverse_buffer(buf_data(enc_buf), buf_size(enc_buf)); + } +} + +static void read_encoding_to_emd(struct encode_match_data *emd, + const struct crunch_options *options) +{ + struct buf enc_buf = STATIC_BUF_INIT; + const char *imported_enc = options->imported_encoding; + + if (imported_enc[0] == '@') + { + struct dec_ctx ctx; + read_encoding_to_buf(imported_enc, options->flags_proto, + !options->direction_forward ^ + (options->write_reverse != 0), &enc_buf); + /* enc_buf is in direction forward which is expected by dec_ctx */ + dec_ctx_init(&ctx, &enc_buf, &enc_buf, NULL, options->flags_proto); + + buf_clear(&enc_buf); + dec_ctx_table_dump(&ctx, &enc_buf); + dec_ctx_free(&ctx); + + imported_enc = buf_data(&enc_buf); + } + + LOG(LOG_NORMAL, (" Using imported encoding\n Enc: %s\n", imported_enc)); + optimal_encoding_import(emd, imported_enc); + + buf_free(&enc_buf); +} + +static struct search_node** +do_compress_backwards(struct match_ctx *ctxp, int ctx_count, + struct encode_match_data *emd, + const struct crunch_options *options, + struct buf *enc) /* IN */ +{ + struct vec snpev = STATIC_VEC_INIT(sizeof(struct match_snp_enum)); + struct match_concat_enum mpcce; + struct search_node **snpp; + int pass, i, last_waltz = 0; + float size; + float old_size; + char prev_enc[100]; + + snpp = calloc(ctx_count, sizeof(struct search_node *)); + + pass = 1; + prev_enc[0] = '\0'; + + LOG(LOG_NORMAL, (" pass %d: ", pass)); + if(options->imported_encoding != NULL) + { + read_encoding_to_emd(emd, options); + if (options->max_passes == 1) + { + /* only one pass, use the greedy variant of search_buffer */ + ++pass; + } + } + else + { + struct vec mpcev = STATIC_VEC_INIT(sizeof(struct match_cache_enum)); + LOG(LOG_NORMAL, ("optimizing ..\n")); + for (i = 0; i < ctx_count; ++i) + { + struct match_cache_enum *mp_enum = vec_push(&mpcev, NULL); + match_cache_get_enum(ctxp + i, mp_enum); + } + match_concat_get_enum(match_cache_enum_get_next, &mpcev, &mpcce); + optimal_optimize(emd, match_concat_enum_get_next, &mpcce); + vec_free(&mpcev, NULL); + } + optimal_encoding_export(emd, enc); + strcpy(prev_enc, buf_data(enc)); + + old_size = 100000000.0; + + for (;;) + { + last_waltz: + size = 0.0; + for (i = 0; i < ctx_count; ++i) + { + if (snpp[i] != NULL) + { + free(snpp[i]); + snpp[i] = NULL; + } + + search_buffer(ctxp + i, optimal_encode, emd, + options->flags_proto, + options->flags_notrait, + options->max_len, + !(pass & 1), &snpp[i]); + if (snpp[i] == NULL) + { + LOG(LOG_ERROR, ("error: search_buffer() returned NULL\n")); + exit(1); + } + size += snpp[i]->total_score; + } + LOG(LOG_NORMAL, (" size %0.1f bits ~%d bytes\n", + size, (((int) size) + 7) >> 3)); + if (last_waltz) + { + break; + } + + ++pass; + if (size >= old_size) + { + last_waltz = 1; + goto last_waltz; + } + old_size = size; + + if(pass > options->max_passes) + { + break; + } + + optimal_free(emd); + optimal_init(emd, options->flags_notrait, options->flags_proto); + + LOG(LOG_NORMAL, (" pass %d: optimizing ..\n", pass)); + + for (i = 0; i < ctx_count; ++i) + { + struct match_snp_enum *mp_enum = vec_push(&snpev, NULL); + match_snp_get_enum(snpp[i], mp_enum); + } + match_concat_get_enum(match_snp_enum_get_next, &snpev, &mpcce); + optimal_optimize(emd, match_concat_enum_get_next, &mpcce); + vec_clear(&snpev, NULL); + + optimal_encoding_export(emd, enc); + if (strcmp(buf_data(enc), prev_enc) == 0) + { + break; + } + strcpy(prev_enc, buf_data(enc)); + } + + vec_free(&snpev, NULL); + + return snpp; +} + +void crunch_multi(struct vec *io_bufs, + struct buf *noread_in, + struct buf *enc_buf, + const struct crunch_options *options, /* IN */ + struct crunch_info *infop) /* OUT */ +{ + struct match_ctx *ctxp; + struct encode_match_data emd; + struct search_node **snpp; + struct crunch_info merged_info = STATIC_CRUNCH_INFO_INIT; + struct buf exported_enc = STATIC_BUF_INIT; + int buf_count = vec_size(io_bufs); + int outlen = 0; + int inlen = 0; + int i; + int *outpos; + + outpos = malloc(sizeof(int) * buf_count); + + ctxp = malloc(sizeof(struct match_ctx) * buf_count); + if(options == NULL) + { + options = &default_options; + } + + LOG(LOG_NORMAL, + ("\nPhase 1: Preprocessing file%s" + "\n---------------------------%s\n", + (buf_count == 1 ? "" : "s"), (buf_count == 1 ? "" : "-"))); + for (i = 0; i < buf_count; ++i) + { + struct buf nor_view; + struct io_bufs *io = vec_get(io_bufs, i); + struct buf *in = &io->in; + const struct buf *nor = NULL; + + if (noread_in != NULL) + { + int growth_req = buf_size(in) + io->in_off - buf_size(noread_in); + if (growth_req > 0) + { + memset(buf_append(noread_in, NULL, growth_req), 0, growth_req); + } + nor = buf_view(&nor_view, noread_in, io->in_off, buf_size(in)); + } + + if (options->direction_forward == 1) + { + struct buf *out = &io->out; + reverse_buffer(buf_data(in), buf_size(in)); + outpos[i] = buf_size(out); + } + inlen += buf_size(in); + match_ctx_init(ctxp + i, in, nor, options->max_len, + options->max_offset, options->favor_speed); + } + LOG(LOG_NORMAL, (" Length of indata: %d bytes.\n", inlen)); + LOG(LOG_NORMAL, (" Preprocessing file%s, done.\n", + (buf_count == 1 ? "" : "s"))); + + emd.out = NULL; + optimal_init(&emd, options->flags_notrait, options->flags_proto); + + LOG(LOG_NORMAL, + ("\nPhase 2: Calculating encoding" + "\n-----------------------------\n")); + snpp = do_compress_backwards(ctxp, buf_count, &emd, options, &exported_enc); + + LOG(LOG_NORMAL, (" Calculating encoding, done.\n")); + + LOG(LOG_NORMAL, + ("\nPhase 3: Generating output file%s" + "\n-------------------------------%s\n", + (buf_count == 1 ? "" : "s"), (buf_count == 1 ? "" : "-"))); + LOG(LOG_NORMAL, (" Enc: %s\n", (char*)buf_data(&exported_enc))); + + if (enc_buf != NULL) + { + struct crunch_options enc_opts = *options; + enc_opts.output_header = 1; + do_output_backwards(NULL, NULL, &emd, &enc_opts, enc_buf, NULL); + + if (options->direction_forward == 1) + { + reverse_buffer(buf_data(enc_buf), buf_size(enc_buf)); + } + } + + for (i = 0; i < buf_count; ++i) + { + struct io_bufs *io = vec_get(io_bufs, i); + const struct buf *in = &io->in; + struct buf *out = &io->out; + struct crunch_info *info = &io->info; + + outlen -= buf_size(out); + do_output_backwards(ctxp + i, snpp[i], &emd, options, out, info); + outlen += buf_size(out); + + if (options->direction_forward == 1) + { + reverse_buffer(buf_data(in), buf_size(in)); + reverse_buffer((char*)buf_data(out) + outpos[i], + buf_size(out) - outpos[i]); + } + + merged_info.traits_used |= info->traits_used; + if (merged_info.max_len < info->max_len) + { + merged_info.max_len = info->max_len; + } + if (merged_info.max_offset < info->max_offset) + { + merged_info.max_offset = info->max_offset; + } + if (merged_info.needed_safety_offset < info->needed_safety_offset) + { + merged_info.needed_safety_offset = info->needed_safety_offset; + } + } + LOG(LOG_NORMAL, (" Length of crunched data: %d bytes.\n", outlen)); + LOG(LOG_BRIEF, (" Crunched data reduced %d bytes (%0.2f%%)\n", + inlen - outlen, 100.0 * (inlen - outlen) / inlen)); + + optimal_free(&emd); + for (i = 0; i < buf_count; ++i) + { + free(snpp[i]); + match_ctx_free(ctxp + i); + } + free(snpp); + free(ctxp); + buf_free(&exported_enc); + free(outpos); + + if(infop != NULL) + { + *infop = merged_info; + } +} + +void reverse_buffer(char *start, int len) +{ + char *end = start + len - 1; + char tmp; + + while (start < end) + { + tmp = *start; + *start = *end; + *end = tmp; + + ++start; + --end; + } +} + +void crunch(struct buf *inbuf, + int in_off, + struct buf *noread_inbuf, + struct buf *outbuf, + const struct crunch_options *options, /* IN */ + struct crunch_info *info) /* OUT */ +{ + struct vec io_bufs = STATIC_VEC_INIT(sizeof(struct io_bufs)); + + struct io_bufs *io = vec_push(&io_bufs, NULL); + io->in = *inbuf; + io->in_off = in_off; + io->out = *outbuf; + crunch_multi(&io_bufs, noread_inbuf, NULL, options, info); + *inbuf = io->in; + *outbuf = io->out; + + vec_free(&io_bufs, NULL); +} + +void decrunch(int level, + struct buf *inbuf, + int in_off, + struct buf *outbuf, + struct decrunch_options *dopts) +{ + struct dec_ctx ctx; + struct buf enc_buf = STATIC_BUF_INIT; + struct buf *encp; + int outpos; + + if (dopts->direction_forward == 0) + { + reverse_buffer(buf_data(inbuf), buf_size(inbuf)); + } + outpos = buf_size(outbuf); + + encp = NULL; + if(dopts->imported_encoding != NULL) + { + read_encoding_to_buf(dopts->imported_encoding, dopts->flags_proto, + !dopts->direction_forward ^ + (dopts->write_reverse != 0), &enc_buf); + /* enc_buf is in direction forward which is expected by dec_ctx */ + encp = &enc_buf; + } + + dec_ctx_init(&ctx, encp, inbuf, outbuf, dopts->flags_proto); + + buf_clear(&enc_buf); + dec_ctx_table_dump(&ctx, &enc_buf); + LOG(level, (" Enc: %s\n", (char*)buf_data(&enc_buf))); + + buf_free(&enc_buf); + dec_ctx_decrunch(&ctx); + dec_ctx_free(&ctx); + + if (dopts->direction_forward == 0) + { + reverse_buffer(buf_data(inbuf), buf_size(inbuf)); + reverse_buffer((char*)buf_data(outbuf) + outpos, + buf_size(outbuf) - outpos); + } +} + +void print_license(void) +{ + LOG(LOG_WARNING, + ("----------------------------------------------------------------------------\n" + "Exomizer v3.1.0 Copyright (c) 2002-2020 Magnus Lind. (magli143@gmail.com)\n" + "----------------------------------------------------------------------------\n")); + LOG(LOG_WARNING, + ("This software is provided 'as-is', without any express or implied warranty.\n" + "In no event will the authors be held liable for any damages arising from\n" + "the use of this software.\n" + "Permission is granted to anyone to use this software, alter it and re-\n" + "distribute it freely for any non-commercial, non-profit purpose subject to\n" + "the following restrictions:\n\n")); + LOG(LOG_WARNING, + (" 1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software in a\n" + " product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\n" + " 2. Altered source versions must be plainly marked as such, and must not\n" + " be misrepresented as being the original software.\n" + " 3. This notice may not be removed or altered from any distribution.\n")); + LOG(LOG_WARNING, + (" 4. The names of this software and/or it's copyright holders may not be\n" + " used to endorse or promote products derived from this software without\n" + " specific prior written permission.\n" + "----------------------------------------------------------------------------\n" + "The files processed and/or generated by using this software are not covered\n" + "nor affected by this license in any way.\n")); +} + +void print_base_flags(enum log_level level, const char *default_outfile) +{ + LOG(level, + (" -o sets the outfile name, default is \"%s\"\n", + default_outfile)); + LOG(level, + (" -q quiet mode, disables all display output\n" + " -B brief mode, disables most display output\n" + " -v displays version and the usage license\n" + " -- treats all following arguments as non-options\n" + " -? displays this help screen\n")); +} + +void print_crunch_flags(enum log_level level, const char *default_outfile) +{ + LOG(level, + (" -c compatibility mode, disables the use of literal sequences\n" + " -C favor compression speed over ratio\n" + " -e uses the given encoding for crunching\n" + " -E don't write the encoding to the outfile\n")); + LOG(level, + (" -m sets the maximum sequence offset, default is 65535\n" + " -M sets the maximum sequence length, default is 65535\n" + " -p limits the number of optimization passes, default is 100\n" + " -T bitfield that controls bit stream traits. [0-7]\n" + " -P bitfield that controls bit stream format. [0-63]\n" + " -N controls addresses that are not to be read.\n")); + print_base_flags(level, default_outfile); +} + +void print_crunch_info(enum log_level level, struct crunch_info *info) +{ + LOG(level, (" Literal sequences are %sused", + info->traits_used & TFLAG_LIT_SEQ ? "" : "not ")); + LOG(level, (", length 1 sequences are %sused", + info->traits_used & TFLAG_LEN1_SEQ ? "" : "not ")); + LOG(level, (",\n length 0123 mirrors are %sused", + info->traits_used & TFLAG_LEN0123_SEQ_MIRRORS ? + "" : "not ")); + LOG(level, (", max length used is %d", info->max_len)); + LOG(level, (",\n max offset used is %d", info->max_offset)); + LOG(level, (" and the safety offset is %d.\n", + info->needed_safety_offset)); +} + +void handle_base_flags(int flag_char, /* IN */ + const char *flag_arg, /* IN */ + print_usage_f *print_usage, /* IN */ + const char *appl, /* IN */ + const char **default_outfilep) /* IN */ +{ + switch(flag_char) + { + case 'o': + *default_outfilep = flag_arg; + break; + case 'q': + LOG_SET_LEVEL(LOG_WARNING); + break; + case 'B': + LOG_SET_LEVEL(LOG_BRIEF); + break; + case 'v': + print_license(); + exit(0); + default: + if (flagflag != '?') + { + LOG(LOG_ERROR, + ("error, invalid option \"-%c\"", flagflag)); + if (flagarg != NULL) + { + LOG(LOG_ERROR, (" with argument \"%s\"", flagarg)); + } + LOG(LOG_ERROR, ("\n")); + } + print_usage(appl, LOG_WARNING, *default_outfilep); + exit(0); + } +} + +void handle_crunch_flags(int flag_char, /* IN */ + const char *flag_arg, /* IN */ + print_usage_f *print_usage, /* IN */ + const char *appl, /* IN */ + struct common_flags *flags) /* OUT */ +{ + struct crunch_options *options = flags->options; + switch(flag_char) + { + case 'c': + options->flags_notrait |= TFLAG_LIT_SEQ; + break; + case 'C': + options->favor_speed = 1; + break; + case 'e': + options->imported_encoding = flag_arg; + break; + case 'E': + options->output_header = 0; + break; + case 'm': + if (str_to_int(flag_arg, &options->max_offset, NULL) != 0 || + options->max_offset < 0 || options->max_offset > 65535) + { + LOG(LOG_ERROR, + ("Error: invalid offset for -m option, " + "must be in the range of [0 - 65535]\n")); + print_usage(appl, LOG_NORMAL, flags->outfile); + exit(1); + } + break; + case 'M': + if (str_to_int(flag_arg, &options->max_len, NULL) != 0 || + options->max_len < 0 || options->max_len >= 65536) + { + LOG(LOG_ERROR, + ("Error: invalid offset for -n option, " + "must be in the range of [0 - 65535]\n")); + print_usage(appl, LOG_NORMAL, flags->outfile); + exit(1); + } + break; + case 'p': + if (str_to_int(flag_arg, &options->max_passes, NULL) != 0 || + options->max_passes < 1 || options->max_passes > 100) + { + LOG(LOG_ERROR, + ("Error: invalid value for -p option, " + "must be in the range of [1 - 100]\n")); + print_usage(appl, LOG_NORMAL, flags->outfile); + exit(1); + } + break; + case 'T': + if (str_to_int(flag_arg, &options->flags_notrait, NULL) != 0 || + options->flags_notrait < 0 || options->flags_notrait > 7) + { + LOG(LOG_ERROR, + ("Error: invalid value for -T option, " + "must be in the range of [0 - 7]\n")); + print_usage(appl, LOG_NORMAL, flags->outfile); + exit(1); + } + break; + case 'P': + do + { + int op = 0; + int flags_proto; + if (*flag_arg == '+') + { + op = 1; + ++flag_arg; + } + else if (*flag_arg == '-') + { + op = 2; + ++flag_arg; + } + if (str_to_int(flag_arg, &flags_proto, &flag_arg) != 0 || + flags_proto < 0 || flags_proto > 63) + { + LOG(LOG_ERROR, + ("Error: invalid value for -P option, " + "must be in the range of [0 - 63]\n")); + print_usage(appl, LOG_NORMAL, flags->outfile); + exit(1); + } + if (op == 1) + { + options->flags_proto |= flags_proto; + } + else if (op == 2) + { + options->flags_proto &= ~flags_proto; + } + else + { + options->flags_proto = flags_proto; + } + } + while (*flag_arg != '\0'); + break; + case 'N': + options->noread_filename = flag_arg; + break; + default: + handle_base_flags(flag_char, flag_arg, print_usage, + appl, &flags->outfile); + } +} diff --git a/loader/tools/exomizer-3.1/src/exo_helper.h b/loader/tools/exomizer-3.1/src/exo_helper.h new file mode 100644 index 0000000..aa8526a --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_helper.h @@ -0,0 +1,152 @@ +#ifndef EXO_HELPER_ALREADY_INCLUDED +#define EXO_HELPER_ALREADY_INCLUDED +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005, 2013, 2015 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "log.h" +#include "buf.h" +#include "search.h" +#include "optimal.h" +#include "flags.h" + +#define DECRUNCH_OPTIONS_DEFAULT {NULL, PFLAG_BITS_ORDER_BE | \ + PFLAG_BITS_COPY_GT_7 | \ + PFLAG_IMPL_1LITERAL | \ + PFLAG_REUSE_OFFSET, \ + 1, 0} + +#define CRUNCH_OPTIONS_DEFAULT {NULL, 100, 65535, 65535, 0, 1, \ + PFLAG_BITS_ORDER_BE | \ + PFLAG_BITS_COPY_GT_7 | \ + PFLAG_IMPL_1LITERAL | \ + PFLAG_REUSE_OFFSET, \ + 0, 0, 0, NULL} + +struct common_flags +{ + struct crunch_options *options; + const char *outfile; +}; + +#define STATIC_CRUNCH_INFO_INIT {0, 0, 0} +struct crunch_info +{ + int traits_used; + int max_len; + int max_offset; + int needed_safety_offset; +}; + +#define CRUNCH_FLAGS "cCe:Em:M:p:P:T:o:N:qBv" +#define BASE_FLAGS "o:qBv" + +void print_crunch_flags(enum log_level level, const char *default_outfile); + +void print_base_flags(enum log_level level, const char *default_outfile); + +void print_crunch_info(enum log_level level, struct crunch_info *info); + +typedef void print_usage_f(const char *appl, enum log_level level, + const char *default_outfile); + +void handle_crunch_flags(int flag_char, /* IN */ + const char *flag_arg, /* IN */ + print_usage_f *print_usage, /* IN */ + const char *appl, /* IN */ + struct common_flags *options); /* OUT */ + +void handle_base_flags(int flag_char, /* IN */ + const char *flag_arg, /* IN */ + print_usage_f *print_usage, /* IN */ + const char *appl, /* IN */ + const char **default_outfilep); /* OUT */ + +struct crunch_options +{ + const char *imported_encoding; + int max_passes; + int max_len; + int max_offset; + int favor_speed; + int output_header; + int flags_proto; + int flags_notrait; + /* 0 backward, 1 forward */ + int direction_forward; + int write_reverse; + const char *noread_filename; +}; + +void print_license(void); + +struct io_bufs +{ + struct buf in; + int in_off; + struct buf out; + struct crunch_info info; +}; + +void crunch_multi(struct vec *io_bufs, + struct buf *noread_in, + struct buf *enc_buf, + const struct crunch_options *options, /* IN */ + struct crunch_info *merged_info); /* OUT */ + +void crunch(struct buf *inbuf, + int in_off, + struct buf *noread_inbuf, + struct buf *outbuf, + const struct crunch_options *options, /* IN */ + struct crunch_info *info); /* OUT */ + +struct decrunch_options +{ + const char *imported_encoding; + /* see crunch_options flags field */ + int flags_proto; + /* 0 backward, 1 forward */ + int direction_forward; + int write_reverse; +}; + +void decrunch(int level, + struct buf *inbuf, + int in_off, + struct buf *outbuf, + struct decrunch_options *dopts); + +void reverse_buffer(char *start, int len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/exo_main.c b/loader/tools/exomizer-3.1/src/exo_main.c new file mode 100644 index 0000000..425f5e0 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_main.c @@ -0,0 +1,1955 @@ +/* + * Copyright (c) 2002 - 2019 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include +#include +#include "log.h" +#include "search.h" +#include "optimal.h" +#include "output.h" +#include "getflag.h" +#include "buf_io.h" +#include "exo_helper.h" +#include "exo_util.h" +#include "parse.h" +#include "named_buffer.h" +#include "desfx.h" +#include "perf.h" + +extern struct buf sfxdecr; + +#define STR2(X) #X +#define STR(X) STR2(X) + +#define DEFAULT_OUTFILE "a.out" + +static void load_plain_file(const char *name, struct buf *mb) +{ + int file_len; + int read_len; + int offset = 0; + int len = 0; + FILE *in; + + in = fopen(name, "rb"); + if(in == NULL) + { + char *p = strrchr(name, ','); + if(p == NULL) + { + /* not found and no comma */ + LOG(LOG_ERROR, ("Error: file not found.\n")); + exit(1); + } + *p = '\0'; + if(str_to_int(p + 1, &offset, NULL)) + { + LOG(LOG_ERROR, ("Error: invalid value for plain file offset.\n")); + exit(1); + } + in = fopen(name, "rb"); + if(in == NULL) + { + p = strrchr(name, ','); + len = offset; + if(len == 0) + { + LOG(LOG_ERROR, ("Error, value for plain file " + "len must not be zero.\n")); + exit(1); + } + *p = '\0'; + if(str_to_int(p + 1, &offset, NULL)) + { + LOG(LOG_ERROR, + ("Error: invalid value for plain file offset.\n")); + exit(1); + } + in = fopen(name, "rb"); + if(in == NULL) + { + /* really not found */ + LOG(LOG_ERROR, ("Error: file not found.\n")); + exit(1); + } + } + } + /* get the real length of the file and validate the offset*/ + if(fseek(in, 0, SEEK_END)) + { + LOG(LOG_ERROR, ("Error: can't seek to EOF.\n")); + fclose(in); + exit(1); + } + file_len = ftell(in); + if(offset < 0) + { + offset += file_len; + } + if(fseek(in, offset, SEEK_SET)) + { + LOG(LOG_ERROR, ("Error: can't seek to offset %d.\n", offset)); + fclose(in); + exit(1); + } + if(len <= 0) + { + len += file_len - offset; + } + if(len < 0 || offset + len > file_len) + { + LOG(LOG_ERROR, ("Error: can't read %d bytes from offset %d.\n", + len, offset)); + fclose(in); + exit(1); + } + LOG(LOG_VERBOSE, ("Reading %d bytes from offset %d.\n", len, offset)); + do + { + char buf[1024]; + int r = 1024 < len? 1024: len; + read_len = fread(buf, 1, r, in); + if(read_len < r) + { + LOG(LOG_ERROR, ("Error: tried to read %d bytes but got %d.\n", + r, read_len)); + fclose(in); + exit(1); + } + buf_append(mb, buf, r); + len -= r; + } + while(len > 0); + fclose(in); +} + +static +int +do_load(const char *file_name, struct buf *mem) +{ + struct load_info info; + unsigned char *p; + + buf_clear(mem); + buf_append(mem, NULL, 65536); + p = buf_data(mem); + + info.basic_txt_start = -1; + + load_located(file_name, p, &info); + + /* move memory to beginning of buffer */ + buf_remove(mem, info.end, -1); + buf_remove(mem, 0, info.start); + + LOG(LOG_NORMAL, (" Crunching from $%04X to $%04X.\n", + info.start, info.end)); + return info.start; +} + +struct target_info +{ + int id; + int sys_token; + int basic_txt_start; + int end_of_ram; + const char *model; + const char *outformat; +}; + +static +int +do_loads(int filec, char *filev[], struct buf *mem, + int basic_txt_start, int sys_token, int trim_sys, + int *basic_var_startp, int *runp, enum file_type *typep) +{ + int run = -1; + int min_start = 65537; + int max_end = -1; + int basic_code = 0; + int i; + unsigned char *p; + struct load_info info; + enum file_type type = RAW; + + + buf_clear(mem); + buf_append(mem, NULL, 65536); + p = buf_data(mem); + + for (i = 0; i < filec; ++i) + { + info.basic_txt_start = basic_txt_start; + load_located(filev[i], p, &info); + run = info.run; + if(run != -1 && runp != NULL) + { + LOG(LOG_DEBUG, ("Propagating found run address $%04X.\n", + info.run)); + *runp = info.run; + } + if (type == RAW) + { + type = info.type; + } + else if (type != info.type) + { + /* mixed types */ + type = UNKNOWN; + } + + /* do we expect any basic file? */ + if(basic_txt_start >= 0) + { + if(info.basic_var_start >= 0) + { + basic_code = 1; + if(basic_var_startp != NULL) + { + *basic_var_startp = info.basic_var_start; + } + if(runp != NULL && run == -1) + { + /* only if we didn't get run address from load_located + * (run is not -1 if we did) */ + int stub_len; + run = find_sys(p + basic_txt_start, sys_token, &stub_len); + *runp = run; + if (trim_sys && + basic_txt_start == info.start && + min_start >= info.start) + { + if (run >= info.start && + run < info.start + stub_len) + { + /* the run address points into the sys stub, + trim up to it but no further */ + info.start = run; + } + else + { + /* trim the sys stub*/ + info.start += stub_len; + } + } + } + } + } + + if (info.start < min_start) + { + min_start = info.start; + } + if (info.end > max_end) + { + max_end = info.end; + } + } + + if(basic_txt_start >= 0 && !basic_code && run == -1) + { + /* no program loaded to the basic start */ + LOG(LOG_ERROR, ("\nError: nothing loaded at the start of basic " + "text address ($%04X).\n", + basic_txt_start)); + exit(1); + } + + /* if we have a basic code loaded and we are doing a proper basic start + * (the caller don't expect a sys address so runp is NULL */ + if(basic_code && runp == NULL) + { + int valuepos = basic_txt_start - 1; + /* the byte immediatley preceeding the basic start must be 0 + * for basic to function properly. */ + if(min_start > valuepos) + { + /* It not covered by the files to crunch. Since the + * default fill value is 0 we don't need to set it but we + * need to include that location in the crunch as well. */ + min_start = valuepos; + } + else + { + int value = p[valuepos]; + /* it has been covered by at least one file. Let's check + * if it is zero. */ + if(value != 0) + { + /* Hm, its not, danger Will Robinson! */ + LOG(LOG_WARNING, + ("Warning, basic will probably not work since the value of" + " the location \npreceeding the basic start ($%04X)" + " is not 0 but %d.\n", valuepos, value)); + } + } + } + + if (typep != NULL) + { + *typep = type; + } + + /* move memory to beginning of buffer */ + buf_remove(mem, max_end, -1); + buf_remove(mem, 0, min_start); + + return min_start; +} + +static +void print_command_usage(const char *appl, enum log_level level) +{ + /* done */ + LOG(level, + ("usage: %s level|mem|sfx|raw|desfx [option]... infile[,
]...\n" + " see the individual commands for more help.\n", + appl)); +} + +static +void print_level_usage(const char *appl, enum log_level level, + const char *default_outfile) +{ + /* done */ + LOG(level, + ("usage: %s level [option]... infile[,
]...\n" + " The level command generates outfiles that are intended to be decrunched on\n" + " the fly while being read.\n", appl)); + LOG(level, + (" -f crunch forward\n")); + print_crunch_flags(level, default_outfile); + LOG(level, + (" All infiles are crunched separately and concatenated in the outfile in the\n" + " order they are given on the command-line.\n")); +} + +static +void print_mem_usage(const char *appl, enum log_level level, + const char *default_outfile) +{ + /* done */ + LOG(level, + ("usage: %s mem [option]... infile[,
]...\n" + " The mem command generates outfiles that are intended to be decrunched from\n" + " memory after being loaded or assembled there.\n", appl)); + LOG(level, + (" -l
adds load address to the outfile, using \"none\" as
\n" + " will skip the load address, defaults to \"auto\".\n")); + LOG(level, + (" -f crunch forward\n")); + print_crunch_flags(level, default_outfile); + LOG(level, + (" All infiles are merged into the outfile. They are loaded in the order\n" + " they are given on the command-line, from left to right.\n")); +} + +static +void print_raw_usage(const char *appl, enum log_level level, + const char *default_out_name) +{ + LOG(level, ("usage: %s [option]... infile\n", appl)); + LOG(level, + (" -b crunch/decrunch backwards instead of forward\n" + " -r write outfile in reverse order\n" + " -d decrunch (instead of crunch)\n")); + print_crunch_flags(level, default_out_name); +} + +static +void print_sfx_usage(const char *appl, enum log_level level, + const char *default_outfile) +{ + /* done */ + LOG(level, + ("usage: %s sfx basic[,[,[,]]]|sys[trim][,]|bin| [option]... infile[,
]...\n" + " The sfx command generates outfiles that are intended to decrunch themselves.\n" + " The basic start argument will start a basic program.\n" + " The sys start argument will auto detect the start address by searching the\n" + " basic start for a sys command.\n" + " The systrim start argument works like the sys start argument but it will\n" + " also trim the sys line from the loaded infile.\n" + , appl)); + LOG(level, + (" the start argument will jmp to the given address.\n" + " -t sets the decruncher target, must be one of 1, 20, 23, 52, 55\n" + " 16, 4, 64, 128, 162 or 168, default is 64\n" + " -X\n" + " -x[1-3]|\n" + " decrunch effect, assembler fragment (don't change X-reg, Y-reg\n" + " or carry) or 1 - 3 for different fast border flash effects\n" + " -n no effect, can't be combined with -X or -x\n")); + LOG(level, + (" -D=\n" + " predefines symbols for the sfx assembler\n" + " -s\n" + " assembler fragment to execute when the decruncher starts.\n" + " (don't change Y-reg)\n" + " -f\n" + " assembler fragment o execute when the decruncher has\n" + " finished\n")); + print_crunch_flags(level, default_outfile); + LOG(level, + (" All infiles are merged into the outfile. They are loaded in the order\n" + " they are given on the command-line, from left to right.\n")); +} + +static +void print_desfx_usage(const char *appl, enum log_level level, + const char *default_outfile) +{ + /* done */ + LOG(level, + ("usage: %s desfx [option]... infile\n" + " The desfx command decrunches files that previously been crunched using the\n" + " sfx command.\n", appl)); + LOG(level, + (" -e
overrides the automatic entry point detection, using \"load\" as\n" + "
sets it to the load address of the infile\n" " -S enables performance statistics output and allows multiple input\n" + " files")); + + print_base_flags(level, default_outfile); +} + +struct io_bufs_located +{ + struct io_bufs io; + int write_location; +}; + +static +void generic(const char *appl, + struct common_flags *flags, + print_usage_f *print_usage, + struct buf *noread_in, + int decrunch_mode, + int located_mode, + int infilec, char *infilev[]) +{ + struct buf name_buf = STATIC_BUF_INIT; + struct buf enc_buf = STATIC_BUF_INIT; + + struct vec entries = STATIC_VEC_INIT(sizeof(struct io_bufs_located)); + struct crunch_options *options = flags->options; + int c; + + if (options->output_header == 0) + { + if (decrunch_mode) + { + LOG(LOG_ERROR, ("Error: Can't combine -E and -d.\n")); + print_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + if (infilec == 0) + { + LOG(LOG_ERROR, ("Error: no input files to process.\n")); + print_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + } + else if (infilec != 1) + { + LOG(LOG_ERROR, ("Error: exactly one input file must be given.\n")); + print_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + else if (located_mode && decrunch_mode) + { + LOG(LOG_ERROR, ("Error: Can't combine located mode and -d.\n")); + print_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + for (c = 0; c < infilec; ++c) + { + struct io_bufs_located *io = vec_push(&entries, NULL); + struct buf *inbuf = &io->io.in; + struct buf *outbuf = &io->io.out; + buf_init(inbuf); + buf_init(outbuf); + io->write_location = -1; + + if (located_mode) + { + io->io.in_off = do_load(infilev[c], inbuf); + io->write_location = io->io.in_off; + if (options->direction_forward == 0) + { + io->write_location += buf_size(inbuf); + } + } + else + { + io->io.in_off = 0; + load_plain_file(infilev[c], inbuf); + LOG(LOG_BRIEF, (" Reading %d bytes from \"%s\".\n", + buf_size(inbuf), infilev[c])); + } + + if(decrunch_mode && options->write_reverse) + { + reverse_buffer(buf_data(inbuf), buf_size(inbuf)); + } + } + + if(decrunch_mode) + { + int inlen; + int outlen; + struct decrunch_options dopts; + struct io_bufs_located *io = vec_get(&entries, 0); + struct buf *inbuf = &io->io.in; + int in_off = io->io.in_off; + struct buf *outbuf = &io->io.out; + + dopts.direction_forward = options->direction_forward; + dopts.write_reverse = options->write_reverse; + dopts.flags_proto = options->flags_proto; + dopts.imported_encoding = options->imported_encoding; + + inlen = buf_size(inbuf); + decrunch(LOG_NORMAL, inbuf, in_off, outbuf, &dopts); + + outlen = buf_size(outbuf); + LOG(LOG_BRIEF, (" Decrunched data expanded %d bytes (%0.2f%%)\n", + outlen - inlen, 100.0 * (outlen - inlen) / inlen)); + } + else + { + struct crunch_info info; + crunch_multi(&entries, noread_in, &enc_buf, options, &info); + + print_crunch_info(LOG_NORMAL, &info); + } + for (c = 0; c < infilec; ++c) + { + struct io_bufs_located *io = vec_get(&entries, c); + struct buf *inbuf = &io->io.in; + struct buf *outbuf = &io->io.out; + const char *p; + + if (io->write_location != -1) + { + if (options->direction_forward == 0) + { + /* append the write location of decrunching */ + unsigned char *p = buf_insert(outbuf, -1, NULL, 2); + p[0] = io->write_location & 255; + p[1] = io->write_location >> 8; + } + else + { + /* prepend the write location of decrunching */ + unsigned char *p = buf_insert(outbuf, 0, NULL, 2); + p[0] = io->write_location >> 8; + p[1] = io->write_location & 255; + } + } + + if(!decrunch_mode && options->write_reverse) + { + reverse_buffer(buf_data(outbuf), buf_size(outbuf)); + } + + p = flags->outfile; + if (options->output_header == 0) + { + buf_clear(&name_buf); + buf_printf(&name_buf, "%s.%02d", flags->outfile, c); + p = buf_data(&name_buf); + } + LOG(LOG_BRIEF, (" Writing %d bytes to \"%s\".\n", + buf_size(outbuf), p)); + write_file(p, outbuf); + + buf_free(outbuf); + buf_free(inbuf); + } + + if (options->output_header == 0) + { + if(options->write_reverse) + { + reverse_buffer(buf_data(&enc_buf), buf_size(&enc_buf)); + } + LOG(LOG_BRIEF, (" Writing encoding to \"%s\".\n", flags->outfile)); + write_file(flags->outfile, &enc_buf); + } + buf_free(&enc_buf); + buf_free(&name_buf); + vec_free(&entries, NULL); +} + +static +void level(const char *appl, int argc, char *argv[]) +{ + char flags_arr[64]; + int c; + int infilec; + char **infilev; + struct buf noread = STATIC_BUF_INIT, *noreadp = NULL; + + struct crunch_options options = CRUNCH_OPTIONS_DEFAULT; + struct common_flags flags = {NULL, DEFAULT_OUTFILE}; + + options.flags_notrait = TFLAG_LEN0123_SEQ_MIRRORS; + flags.options = &options; + options.write_reverse = 1; + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "f%s", CRUNCH_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch (c) + { + case 'f': + options.direction_forward = 1; + options.write_reverse = 0; + break; + default: + handle_crunch_flags(c, flagarg, print_level_usage, appl, &flags); + } + } + + + if (options.noread_filename != NULL) + { + struct load_info info; + unsigned char *p = + memset(buf_append(&noread, NULL, 65536), 0, 65536); + load_located(options.noread_filename, p, &info); + noreadp = &noread; + } + + + infilev = argv + flagind; + infilec = argc - flagind; + + if (options.output_header == 0) + { + generic(appl, + &flags, + print_level_usage, + noreadp, + 0, + 1, + infilec, infilev); + } + else + { + struct crunch_info total = STATIC_CRUNCH_INFO_INIT; + struct buf in; + struct buf out; + + if (infilec == 0) + { + LOG(LOG_ERROR, ("Error: no input files to process.\n")); + print_level_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + buf_init(&in); + buf_init(&out); + + /* append the files instead of merging them */ + for(c = 0; c < infilec; ++c) + { + struct crunch_info info; + int in_load; + int in_len; + int out_pos; + out_pos = buf_size(&out); + + in_load = do_load(infilev[c], &in); + in_len = buf_size(&in); + + if(options.direction_forward) + { + /* append the starting address of decrunching */ + buf_append_char(&out, in_load >> 8); + buf_append_char(&out, in_load & 255); + } + + crunch(&in, in_load, noreadp, &out, &options, &info); + + if(!options.direction_forward) + { + /* append the starting address of decrunching */ + buf_append_char(&out, (in_load + in_len) & 255); + buf_append_char(&out, (in_load + in_len) >> 8); + + /* reverse the just appended segment of the out buffer */ + reverse_buffer((char*)buf_data(&out) + out_pos, + buf_size(&out) - out_pos); + } + + total.traits_used |= info.traits_used; + if (info.max_len > total.max_len) + { + total.max_len = info.max_len; + } + if(info.needed_safety_offset > total.needed_safety_offset) + { + total.needed_safety_offset = info.needed_safety_offset; + } + } + + print_crunch_info(LOG_NORMAL, &total); + + LOG(LOG_BRIEF, (" Writing %d bytes to \"%s\".\n", + buf_size(&out), flags.outfile)); + write_file(flags.outfile, &out); + + buf_free(&out); + buf_free(&in); + } + + buf_free(&noread); +} + +static +void mem(const char *appl, int argc, char *argv[]) +{ + char flags_arr[64]; + int load_addr = -1; + int load_addr_given = 0; + int prepend_load_addr = 1; + int c; + int infilec; + char **infilev; + struct buf noread = STATIC_BUF_INIT, *noreadp = NULL; + + struct crunch_options options = CRUNCH_OPTIONS_DEFAULT; + struct common_flags flags = {NULL, DEFAULT_OUTFILE}; + + options.flags_notrait = TFLAG_LEN0123_SEQ_MIRRORS; + flags.options = &options; + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "fl:%s", CRUNCH_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch(c) + { + case 'f': + options.direction_forward = 1; + break; + case 'l': + load_addr_given = 1; + if(strcmp(flagarg, "none") == 0) + { + prepend_load_addr = 0; + } + else if(strcmp(flagarg, "auto") == 0) + { + load_addr = -1; + } + else if(str_to_int(flagarg, &load_addr, NULL) != 0 || + load_addr < 0 || load_addr >= 65536) + { + LOG(LOG_ERROR, + ("Error: invalid address for -l option, " + "must be in the range of [0 - 0xffff]\n")); + print_mem_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + break; + default: + handle_crunch_flags(c, flagarg, print_mem_usage, appl, &flags); + } + } + + if (options.noread_filename != NULL) + { + struct load_info info; + unsigned char *p = + memset(buf_append(&noread, NULL, 65536), 0, 65536); + load_located(options.noread_filename, p, &info); + noreadp = &noread; + } + + infilev = argv + flagind; + infilec = argc - flagind; + + if (options.output_header == 0) + { + if (load_addr_given && prepend_load_addr) + { + LOG(LOG_ERROR, ("Error: -E implies -lnone and can't be " + "combined with a load address.\n")); + print_mem_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + generic(appl, + &flags, + print_mem_usage, + noreadp, + 0, + 1, + infilec, infilev); + } + else + { + struct buf in; + struct buf out; + struct crunch_info info; + int in_load; + int in_len; + int safety; + + if (infilec == 0) + { + LOG(LOG_ERROR, ("Error: no input files to process.\n")); + print_mem_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + buf_init(&in); + buf_init(&out); + + in_load = do_loads(infilec, infilev, &in, -1, -1, 0, NULL, NULL, NULL); + in_len = buf_size(&in); + + LOG(LOG_NORMAL, (" Crunching from $%04X to $%04X.\n", + in_load, in_load + in_len)); + + /* make room for load addr */ + if(prepend_load_addr) + { + buf_append(&out, NULL, 2); + } + + if(options.direction_forward) + { + /* append the in_loading address of decrunching */ + buf_append_char(&out, in_load >> 8); + buf_append_char(&out, in_load & 255); + } + + crunch(&in, in_load, noreadp, &out, &options, &info); + safety = info.needed_safety_offset; + + if(!options.direction_forward) + { + /* append the in_loading address of decrunching */ + buf_append_char(&out, (in_load + in_len) & 255); + buf_append_char(&out, (in_load + in_len) >> 8); + } + + /* prepend load addr */ + if(prepend_load_addr) + { + char *p; + if(load_addr < 0) + { + /* auto load addr specified */ + load_addr = in_load; + if(options.direction_forward) + { + load_addr += in_len + safety - buf_size(&out) + 2; + } + else + { + load_addr -= safety; + } + } + p = buf_data(&out); + p[0] = load_addr & 255; + p[1] = load_addr >> 8; + LOG(LOG_NORMAL, (" The load address is $%04X - $%04X.\n", + load_addr, load_addr + buf_size(&out) - 2)); + } + else + { + LOG(LOG_NORMAL, (" No load address, data length is $%04X.\n", + buf_size(&out))); + } + + print_crunch_info(LOG_NORMAL, &info); + + if (prepend_load_addr) + { + LOG(LOG_BRIEF, + (" Writing \"%s\" as prg, saving from $%04X to $%04X.\n", + flags.outfile, load_addr, + load_addr + buf_size(&out) - 2)); + } + else + { + LOG(LOG_BRIEF, (" Writing %d bytes to \"%s\".\n", + buf_size(&out), flags.outfile)); + } + write_file(flags.outfile, &out); + + buf_free(&out); + buf_free(&in); + } + buf_free(&noread); +} + +static +const struct target_info * +get_target_info(int target) +{ + static const struct target_info targets[] = + { + {1, 0xbf, 0x0501, 0x10000, "Oric", "tap"}, + {20, 0x9e, 0x1001, 0x2000, "Vic20", "prg"}, + {23, 0x9e, 0x0401, 0x2000, "Vic20+3kB", "prg"}, + {52, 0x9e, 0x1201, 0x8000, "Vic20+32kB", "prg"}, + {55, 0x9e, 0x1201, 0x8000, "Vic20+3kB+32kB", "prg"}, + {16, 0x9e, 0x1001, 0x4000, "C16", "prg"}, + {4, 0x9e, 0x1001, 0xfd00, "plus4", "prg"}, + {64, 0x9e, 0x0801, 0x10000, "C64", "prg"}, + {128, 0x9e, 0x1c01, 0xff00, "C128", "prg"}, + {162, 0x8c, 0x0801, 0xc000, "Apple ][+", "AppleSingle"}, + {168, -1, 0x2000, 0xd000, "Atari 400/800 XL/XE", "xex"}, + {4032, 0x9e, 0x0401, 0x8000, "PET CBM 4032", "prg"}, + {0xbbcb, -1, 0x1902, 0x8000, "BBC Micro B", "BBCIm/BBCXfer inf"}, + {0, -1, -1, -1, NULL, NULL} + }; + const struct target_info *targetp; + for(targetp = targets; targetp->id != 0; ++targetp) + { + if(target == targetp->id) + { + break; + } + } + if(targetp->id == 0) + { + targetp = NULL; + } + return targetp; +} + +static void do_effect(const char *appl, int no_effect, const char *fast, + const char *slow) +{ + struct buf *fx = NULL; + + if(no_effect + (fast != NULL) + (slow != NULL) > 1) + { + LOG(LOG_ERROR, + ("Error: can't combine any of the -n, -x or -X flags.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + if(no_effect) + { + set_initial_symbol("i_effect", -1); + } + else if(fast != NULL) + { + int value; + if(str_to_int(fast, &value, NULL) == 0) + { + if(value == 1) set_initial_symbol("i_effect", 1); + else if(value == 2) set_initial_symbol("i_effect", 2); + else if(value == 3) set_initial_symbol("i_effect", 3); + else + { + LOG(LOG_ERROR, + ("Error: invalid range for effect shorthand, " + "must be in the range of [1 - 3]\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + } + else + { + set_initial_symbol("i_effect_custom", 1); + fx = new_initial_named_buffer("effect_custom"); + buf_append(fx, fast, strlen(fast)); + } + set_initial_symbol("i_effect_speed", 1); + } + else if(slow != NULL) + { + int value; + if(str_to_int(slow, &value, NULL) == 0) + { + LOG(LOG_ERROR, ("Error: Can't use shorthand for -X flag.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + else + { + set_initial_symbol("i_effect_custom", 1); + fx = new_initial_named_buffer("effect_custom"); + buf_append(fx, slow, strlen(slow)); + set_initial_symbol("i_effect_speed", 0); + } + } + else + { + set_initial_symbol("i_effect", 0); + set_initial_symbol("i_effect_speed", 0); + } +} + +static +void sfx(const char *appl, int argc, char *argv[]) +{ + int in_load; + int in_len; + int basic_txt_start = -1; + int basic_var_start = -1; + int basic_highest_addr = -1; + int decr_target = 64; + int decr_target_set = 0; + int entry_addr = -1; + int trim_sys = 0; + int no_effect = 0; + const char *fast = NULL; + const char *slow = NULL; + const char *enter_custom = NULL; + const char *exit_custom = NULL; + char flags_arr[64]; + int c; + int infilec; + char **infilev; + + struct crunch_info info; + + struct crunch_options options = CRUNCH_OPTIONS_DEFAULT; + struct common_flags flags = {NULL, DEFAULT_OUTFILE}; + const struct target_info *targetp; + + struct buf buf1; + + struct buf *in; + struct buf *out; + + options.flags_proto = PFLAG_BITS_ORDER_BE | PFLAG_BITS_COPY_GT_7 | + PFLAG_REUSE_OFFSET; + flags.options = &options; + + if(argc <= 1) + { + LOG(LOG_ERROR, ("Error: no start argument given.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + parse_init(); + + /* required argument: how to start the crunched program */ + do + { + char *p = strtok(argv[1], ","); + if (strcmp(p, "sys") == 0 || strcmp(p, "systrim") == 0) + { + if (strcmp(p, "systrim") == 0) + { + trim_sys = 1; + } + /* we should look for a basic sys command. */ + entry_addr = -1; + p = strtok(NULL, ","); + /* look for an optional basic start address */ + if(p == NULL) break; + if(str_to_int(p, &basic_txt_start, NULL) != 0) + { + LOG(LOG_ERROR, + ("Error: invalid value for the start of basic text " + "address.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + } + else if(strcmp(p, "basic") == 0) + { + /* we should start a basic program. */ + entry_addr = -2; + p = strtok(NULL, ","); + /* look for an optional basic start address */ + if(p == NULL) break; + if(str_to_int(p, &basic_txt_start, NULL) != 0) + { + LOG(LOG_ERROR, + ("Error: invalid value for the start of basic text " + "address.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + p = strtok(NULL, ","); + /* look for an optional basic end address */ + if(p == NULL) break; + if(str_to_int(p, &basic_var_start, NULL) != 0) + { + LOG(LOG_ERROR, + ("Error: invalid value for the start of basic " + "variables address.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + p = strtok(NULL, ","); + /* look for an optional highest address used by basic */ + if(p == NULL) break; + if(str_to_int(p, &basic_highest_addr, NULL) != 0) + { + LOG(LOG_ERROR, + ("Error: invalid value for the highest address used " + "by basic address.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + } + else if (strcmp(p, "bin") == 0) + { + entry_addr = -3; + } + else if(str_to_int(p, &entry_addr, NULL) != 0 || + entry_addr < 0 || entry_addr >= 65536) + { + /* we got an address we should jmp to. */ + LOG(LOG_ERROR, + ("Error: invalid address for , " + "must be in the range of [0 - 0xffff]\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + } + while(0); + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "nD:t:x:X:s:f:%s", CRUNCH_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + char *p; + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch(c) + { + case 't': + if (str_to_int(flagarg, &decr_target, NULL) != 0 || + get_target_info(decr_target) == NULL) + { + LOG(LOG_ERROR, + ("error: invalid value, %d, for -t option, must be one of " + "1, 20, 23, 52, 55, 16, 4, 64, 128, 162, 168, 4032 or " + "48075.\n", + decr_target)); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + decr_target_set = 1; + break; + case 'n': + no_effect = 1; + break; + case 'x': + fast = flagarg; + break; + case 'X': + slow = flagarg; + break; + case 's': + enter_custom = flagarg; + break; + case 'f': + exit_custom = flagarg; + break; + case 'D': + p = strrchr(flagarg, '='); + if(p != NULL) + { + int value; + if(str_to_int(p + 1, &value, NULL) != 0) + { + LOG(LOG_ERROR, ("Error: invalid value for -D " + "[=] option.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + /* This is ugly, we really should allocate our own + * copy of the symbol string. */ + *p = '\0'; + set_initial_symbol(flagarg, value); + } + else + { + LOG(LOG_ERROR, ("Error: invalid value for -D " + "= option.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + break; + default: + handle_crunch_flags(c, flagarg, print_sfx_usage, appl, &flags); + } + } + + { + int required = PFLAG_BITS_ORDER_BE | PFLAG_BITS_COPY_GT_7; + int unsupported = PFLAG_BITS_ALIGN_START | PFLAG_IMPL_1LITERAL; + + if ((options.flags_proto & required) != required) + { + LOG(LOG_ERROR, + ("Warning: -P bits " STR(PBIT_BITS_ORDER_BE) + " and " STR(PBIT_BITS_COPY_GT_7) + " are required by sfx, setting them.\n")); + options.flags_proto |= required; + } + if ((options.flags_proto & unsupported) != 0) + { + LOG(LOG_ERROR, + ("Warning: -P bits " STR(PBIT_BITS_ALIGN_START) + " and " STR(PBIT_IMPL_1LITERAL) + " are not supported by sfx, clearing them.\n")); + options.flags_proto &= ~unsupported; + } + options.flags_notrait |= TFLAG_LEN0123_SEQ_MIRRORS; + } + + if (options.flags_proto & PFLAG_4_OFFSET_TABLES) + { + set_initial_symbol("i_fourth_offset_table", 1); + } + if (options.flags_proto & PFLAG_REUSE_OFFSET) + { + set_initial_symbol("i_reuse_offset", 1); + } + + do_effect(appl, no_effect, fast, slow); + if(enter_custom != NULL) + { + set_initial_symbol("i_enter_custom", 1); + buf_append(new_initial_named_buffer("enter_custom"), + enter_custom, strlen(enter_custom)); + } + if(exit_custom != NULL) + { + set_initial_symbol("i_exit_custom", 1); + buf_append(new_initial_named_buffer("exit_custom"), + exit_custom, strlen(exit_custom)); + } + + buf_init(&buf1); + in = &buf1; + out = new_initial_named_buffer("crunched_data"); + + infilev = argv + flagind + 1; + infilec = argc - flagind - 1; + + if (infilec == 0) + { + LOG(LOG_ERROR, ("Error: no input files to process.\n")); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + targetp = get_target_info(decr_target); + if(entry_addr == -2 && (targetp->id == 0xa8 || targetp->id == 0xbbcb)) + { + /* basic start not implemented for Atari or BBC targets */ + LOG(LOG_ERROR, ("Start address \"basic\" is not supported for " + "the %s target.\n", targetp->model)); + print_sfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + if(basic_txt_start == -1) + { + basic_txt_start = targetp->basic_txt_start; + } + + { + int safety; + int *basic_var_startp; + int *entry_addrp; + int basic_start; + int mode_bin = 0; + enum file_type type; + struct buf noread = STATIC_BUF_INIT, *noreadp = NULL; + + if (options.noread_filename != NULL) + { + struct load_info info; + unsigned char *p = + memset(buf_append(&noread, NULL, 65536), 0, 65536); + load_located(options.noread_filename, p, &info); + noreadp = &noread; + } + + entry_addrp = NULL; + basic_var_startp = NULL; + if(entry_addr == -2 && basic_var_start == -1) + { + basic_var_startp = &basic_var_start; + } + basic_start = -1; + if (entry_addr == -1) + { + /* mode sys */ + basic_start = basic_txt_start; + entry_addrp = &entry_addr; + } + else if (entry_addr == -2) + { + /* mode basic */ + basic_start = basic_txt_start; + } + else if (entry_addr == -3) + { + /* mode bin */ + entry_addrp = &entry_addr; + mode_bin = 1; + } + + in_load = do_loads(infilec, infilev, in, + basic_start, targetp->sys_token, trim_sys, + basic_var_startp, entry_addrp, &type); + + /* if "sfx bin or explicit run address given */ + if (!decr_target_set && (mode_bin || entry_addr >= 0)) + { + /* target is not set explicitly, auto detect from file type */ + switch (type) + { + case ATARI_XEX: + decr_target = 0xa8; + break; + case ORIC_TAP: + decr_target = 0x1; + break; + case APPLESINGLE_SYS: + case APPLESINGLE: + decr_target = 0xa2; + break; + case BBC_INF: + decr_target = 0xbbcb; + break; + default: + break; + } + /* refetch target info */ + targetp = get_target_info(decr_target); + } + if (mode_bin) + { + set_initial_symbol_soft("i_load_addr", in_load); + if (decr_target == 0xa2 && type == APPLESINGLE_SYS) + { + /* magic for indicating Apple ][ PRODOS system file */ + set_initial_symbol_soft("i_a2_file_type", 0xff); + } + } + in_len = buf_size(in); + + if(in_load + in_len > targetp->end_of_ram) + { + LOG(LOG_ERROR, ("Error:\n The memory of the %s target ends at " + "$%04X and can't hold the\n uncrunched data " + "that covers $%04X to $%04X.\n", + targetp->model, targetp->end_of_ram, + in_load, in_load + in_len)); + exit(1); + } + + LOG(LOG_NORMAL, (" Crunching from $%04X to $%04X.\n", + in_load, in_load + in_len)); + + if(decr_target == 20 || decr_target == 52) + { + /* these are vic20 targets with a memory hole from + * $0400-$1000. Each page is filled with the value of the + * high-byte of its address. */ + if(in_load >= 0x0400 && in_load + in_len <= 0x1000) + { + /* all the loaded data is in the memory hole.*/ + LOG(LOG_ERROR, + ("Error: all data loaded to the memory hole.\n")); + exit(1); + } + else if(in_load >= 0x0400 && in_load < 0x1000 && + in_load + in_len > 0x1000) + { + /* The data starts in the memory hole and ends in + * RAM. We need to adjust the start. */ + int diff = 0x1000 - in_load; + in_load += diff; + in_len -= diff; + buf_remove(in, 0, diff); + LOG(LOG_WARNING, + ("Warning, trimming address interval to $%04X-$%04X.\n", + in_load, in_load + in_len)); + } + else if(in_load < 0x0400 && + in_load + in_len >= 0x0400 && in_load + in_len < 0x1000) + { + /* The data starts in RAM and ends in the memory + * hole. We need to adjust the end. */ + int diff = in_load + in_len - 0x0400; + in_len -= diff; + buf_remove(in, in_len, -1); + LOG(LOG_WARNING, + ("Warning, trimming address interval to $%04X-$%04X.\n", + in_load, in_load + in_len)); + } + else if(in_load < 0x0400 && in_load + in_len > 0x1000) + { + /* The data starts in RAM covers the memory hole and + * ends in RAM. */ + int hi, lo; + char *p; + p = buf_data(in); + for(hi = 0x04; hi < 0x10; hi += 0x01) + { + for(lo = 0; lo < 256; ++lo) + { + int addr = (hi << 8) | lo; + p[addr - in_load] = hi; + } + } + LOG(LOG_NORMAL, ("Memory hole at interval $0400-$1000 " + "included in crunch..\n")); + } + } + + /* make room for load addr */ + buf_append(out, NULL, 2); + + crunch(in, in_load, noreadp, out, &options, &info); + + print_crunch_info(LOG_NORMAL, &info); + + safety = info.needed_safety_offset; + + /* append the in_loading address of decrunching */ + buf_append_char(out, (in_load + in_len) & 255); + buf_append_char(out, (in_load + in_len) >> 8); + + /* prepend load addr */ + { + char *p; + p = buf_data(out); + p[0] = (in_load - safety) & 255; + p[1] = (in_load - safety) >> 8; + } + + buf_free(&noread); + } + + LOG(LOG_NORMAL, (" Target is self-decrunching %s executable", + targetp->model)); + if(entry_addr == -1) + { + LOG(LOG_ERROR, ("\nError: can't find sys address (token $%02X)" + " at basic text start ($%04X).\n", + targetp->sys_token, basic_txt_start)); + exit(1); + } + if(entry_addr != -2) + { + LOG(LOG_NORMAL, (",\n jmp address $%04X.\n", entry_addr)); + } + else + { + LOG(LOG_NORMAL, (",\n basic start ($%04X-$%04X).\n", + basic_txt_start, basic_var_start)); + } + + { + /* add decruncher */ + struct decrunch_options dopts = DECRUNCH_OPTIONS_DEFAULT; + struct buf source; + + buf_init(&source); + decrunch(LOG_DEBUG, &sfxdecr, 0, &source, &dopts); + + in = out; + out = &buf1; + buf_clear(out); + + set_initial_symbol("r_start_addr", entry_addr); + /*initial_symbol_dump( LOG_NORMAL, "r_start_addr");*/ + set_initial_symbol("r_target", decr_target); + /*initial_symbol_dump( LOG_NORMAL, "r_target");*/ + set_initial_symbol("r_in_load", in_load); + /*initial_symbol_dump( LOG_NORMAL, "r_in_addr");*/ + set_initial_symbol("r_in_len", in_len); + /*initial_symbol_dump( LOG_NORMAL, "r_in_len");*/ + + if(entry_addr == -2) + { + /* only set this if its changed from the default. */ + if(basic_txt_start != targetp->basic_txt_start) + { + set_initial_symbol("i_basic_txt_start", basic_txt_start); + initial_symbol_dump( LOG_DEBUG, "i_basic_txt_start"); + } + /* only set this if we've been given a value for it. */ + if(basic_var_start != -1) + { + set_initial_symbol("i_basic_var_start", basic_var_start); + initial_symbol_dump(LOG_DEBUG, "i_basic_var_start"); + } + /* only set this if we've been given a value for it. */ + if(basic_highest_addr != -1) + { + set_initial_symbol("i_basic_highest_addr", basic_highest_addr); + initial_symbol_dump(LOG_DEBUG, "i_basic_highest_addr"); + } + } + + if(info.traits_used & TFLAG_LIT_SEQ) + { + set_initial_symbol("i_literal_sequences_used", 1); + initial_symbol_dump(LOG_DEBUG, "i_literal_sequences_used"); + } + if(info.max_len <= 256) + { + set_initial_symbol("i_max_sequence_length_256", 1); + initial_symbol_dump(LOG_DEBUG, "i_max_sequence_length_256"); + } + + if(assemble(&source, out) != 0) + { + LOG(LOG_ERROR, ("Parse failure.\n")); + exit(1); + } + else + { + int table_size = (16+4+16+16)*3; + i32 lowest_addr; + i32 max_transfer_len; + i32 lowest_addr_out; + i32 highest_addr_out; + i32 i_table_addr; + i32 stage3end, zp_len_lo, zp_len_hi, zp_src_lo, zp_bits_hi; + i32 zp_ro_state, i_effect; + i32 i_ram_enter, i_ram_during, i_ram_exit; + i32 i_irq_enter, i_irq_during, i_irq_exit; + i32 i_nmi_enter, i_nmi_during, i_nmi_exit; + i32 c_effect_color; + + if (options.flags_proto & PFLAG_4_OFFSET_TABLES) + { + table_size = (16+4+16+16+16)*3; + } + + resolve_symbol("lowest_addr", NULL, &lowest_addr); + resolve_symbol("max_transfer_len", NULL, &max_transfer_len); + resolve_symbol("lowest_addr_out", NULL, &lowest_addr_out); + resolve_symbol("highest_addr_out", NULL, &highest_addr_out); + resolve_symbol("i_table_addr", NULL, &i_table_addr); + resolve_symbol("stage3end", NULL, &stage3end); + resolve_symbol("zp_len_lo", NULL, &zp_len_lo); + resolve_symbol("zp_len_hi", NULL, &zp_len_hi); + resolve_symbol("zp_src_lo", NULL, &zp_src_lo); + resolve_symbol("zp_bits_hi", NULL, &zp_bits_hi); + if (options.flags_proto & PFLAG_REUSE_OFFSET) + { + resolve_symbol("zp_ro_state", NULL, &zp_ro_state); + } + resolve_symbol("i_effect2", NULL, &i_effect); + resolve_symbol("i_irq_enter", NULL, &i_irq_enter); + resolve_symbol("i_irq_during", NULL, &i_irq_during); + resolve_symbol("i_irq_exit", NULL, &i_irq_exit); + + if (stage3end > 0x1f2) + { + LOG(LOG_ERROR, + ("ERROR: The generated decruncher is too b" + "ig. It will be overwritten by its own\n" + "stack usage. Please use options or disab" + "le features to make it smaller.\n.")); + exit(1); + } + LOG(LOG_BRIEF, (" Writing \"%s\" as %s, saving from " + "$%04X to $%04X.\n", flags.outfile, + targetp->outformat, lowest_addr_out, + highest_addr_out)); + + LOG(LOG_NORMAL, ("Memory layout: |Start |End |\n")); + LOG(LOG_NORMAL, (" Crunched data | $%04X| $%04X|\n", + lowest_addr, lowest_addr + max_transfer_len)); + LOG(LOG_NORMAL, (" Decrunched data | $%04X| $%04X|\n", + in_load, in_load + in_len)); + LOG(LOG_NORMAL, (" Decrunch table | $%04X| $%04X|\n", + i_table_addr, i_table_addr + table_size)); + LOG(LOG_NORMAL, (" Decruncher | $00FD| $%04X| and ", + stage3end)); + if (options.flags_proto & PFLAG_REUSE_OFFSET) + { + LOG(LOG_NORMAL, + ("$%02X,$%02X,$%02X,$%02X,$%02X,$%02X\n", zp_bits_hi, + zp_len_lo, zp_len_hi, zp_src_lo, zp_src_lo + 1, + zp_ro_state)); + } + else + { + LOG(LOG_NORMAL, + ("$%02X,$%02X,$%02X,$%02X,$%02X\n", zp_bits_hi, + zp_len_lo, zp_len_hi, zp_src_lo, zp_src_lo + 1)); + } + if(i_effect == 0 && !resolve_symbol("i_effect_custom", NULL, NULL)) + { + resolve_symbol("c_effect_color", NULL, &c_effect_color); + LOG(LOG_NORMAL, (" Decrunch effect writes to $%04X.\n", + c_effect_color)); + } + LOG(LOG_NORMAL, ("Decruncher: |Enter |During|Exit |\n")); + if (decr_target == 1 || decr_target == 64 || decr_target == 128 || + decr_target == 4 || decr_target == 16 || decr_target == 168) + { + resolve_symbol("i_ram_enter", NULL, &i_ram_enter); + resolve_symbol("i_ram_during", NULL, &i_ram_during); + resolve_symbol("i_ram_exit", NULL, &i_ram_exit); + LOG(LOG_NORMAL, (" RAM config | $%02X| $%02X| $%02X|\n", + i_ram_enter, i_ram_during, i_ram_exit)); + } + LOG(LOG_NORMAL, (" IRQ enabled | %3d| %3d| %3d|\n", + i_irq_enter, i_irq_during, i_irq_exit)); + if (decr_target == 168) + { + resolve_symbol("i_nmi_enter", NULL, &i_nmi_enter); + resolve_symbol("i_nmi_during", NULL, &i_nmi_during); + resolve_symbol("i_nmi_exit", NULL, &i_nmi_exit); + LOG(LOG_NORMAL, (" NMI enabled | $%02X| $%02X| $%02X|\n", + i_nmi_enter, i_nmi_during, i_nmi_exit)); + } + else if (decr_target == 0xbbcb) + { + FILE *inf; + char *p; + int i; + /* write shadowing .inf file */ + struct buf name_buf = STATIC_BUF_INIT; + buf_printf(&name_buf, "%s.inf", flags.outfile); + p = buf_data(&name_buf); + inf = fopen(p, "wb"); + p[buf_size(&name_buf) - 4] = '\0'; + for (i = 0; p[i] != '\0' && (i < 7 || (p[i] = '\0')); ++i) + { + p[i] = toupper(p[i]); + } + + fprintf(inf, "$.%s %06X %06X %X", p, + lowest_addr_out | 0xff0000, + lowest_addr_out | 0xff0000, + buf_size(out)); + fclose(inf); + } + } + + buf_free(&source); + } + + write_file(flags.outfile, out); + buf_free(&buf1); + + parse_free(); +} + +static +void raw(const char *appl, int argc, char *argv[]) +{ + char flags_arr[64]; + int decrunch_mode = 0; + int c, infilec; + char **infilev; + struct buf noread = STATIC_BUF_INIT, *noreadp = NULL; + + struct crunch_options options = CRUNCH_OPTIONS_DEFAULT; + struct common_flags flags = {NULL, DEFAULT_OUTFILE}; + + flags.options = &options; + options.direction_forward = 1; + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "brd%s", CRUNCH_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch (c) + { + case 'b': + options.direction_forward = 0; + break; + case 'r': + options.write_reverse = 1; + break; + case 'd': + decrunch_mode = 1; + break; + default: + handle_crunch_flags(c, flagarg, print_raw_usage, appl, &flags); + } + } + + if (options.noread_filename != NULL) + { + load_plain_file(options.noread_filename, &noread); + noreadp = &noread; + } + + infilev = argv + flagind; + infilec = argc - flagind; + + generic(appl, + &flags, + print_raw_usage, + noreadp, + decrunch_mode, + 0, + infilec, infilev); + + buf_free(&noread); +} + +static +void desfx(const char *appl, int argc, char *argv[]) +{ + struct perf_ctx perf = STATIC_PERF_INIT; + struct buf buf = STATIC_BUF_INIT; + char flags_arr[64]; + int i, c, infilec; + char **infilev; + int entry = -1; + int stat = 0; + + const char *outfile = DEFAULT_OUTFILE; + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "e:S%s", BASE_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch (c) + { + case 'e': + if(strcmp(flagarg,"load") == 0) + { + entry = -2; + } + else if(str_to_int(flagarg, &entry, NULL) != 0 || + entry < 0 || entry >= 65536) + { + LOG(LOG_ERROR,("Error: invalid address for -e option, " + "must be in the range of [0 - 0xffff]\n")); + print_desfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + break; + case 'S': + stat = 1; + break; + default: + handle_base_flags(c, flagarg, print_desfx_usage, appl, &outfile); + } + } + + infilev = argv + flagind; + infilec = argc - flagind; + + if (stat == 0 && infilec != 1) + { + LOG(LOG_ERROR, ("Error: only one input file must be given.\n")); + print_desfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + else if (infilec < 1) + { + LOG(LOG_ERROR, ("Error: at least one input file must be given.\n")); + print_desfx_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + for (i = 0; i < infilec; ++i) + { + int entry1 = entry; + struct load_info info; + struct buf mem; + u8 *p; + int start; + int end; + u32 cycles; + int cookedend; + const char *n; + + buf_init(&mem); + buf_append(&mem, NULL, 65536); + + p = buf_data(&mem); + + /* load file, don't care about tracking basic */ + info.basic_txt_start = -1; + load_located(infilev[i], p, &info); + + if(entry1 == -1) + { + /* use detected address */ + entry1 = info.run; + } + else if(entry1 == -2) + { + /* use load address */ + entry1 = info.start; + } + + /* no start address from load */ + if(entry1 == -1) + { + /* look for sys line */ + entry1 = find_sys(p + info.start, -1, NULL); + } + if(entry1 == -1) + { + LOG(LOG_ERROR, ("Error, can't find entry1 point.\n")); + exit(1); + } + + entry1 = decrunch_sfx(p, entry1, &start, &end, &cycles); + + /* change 0x0 into 0x10000 */ + cookedend = ((end - 1) & 0xffff) + 1; + + if (stat != 0) + { + int inlen = info.end - info.start; + int outlen = end - start; + perf_add(&perf, infilev[i], inlen, outlen, cycles); + } + else + { + LOG(LOG_NORMAL, + (" Decrunch took %0.6f Mcycles, " + "speed was %0.2f kB/Mc (%0.2f c/B).\n", + (double)cycles / 1000000, + 1000 * (end - start) / (float)cycles, + (float)cycles / (end - start))); + } + buf_remove(&mem, cookedend, -1); + buf_remove(&mem, 0, start); + buf_insert(&mem, 0, NULL, 2); + + p = buf_data(&mem); + p[0] = start; + p[1] = start >> 8; + + n = outfile; + if (stat != 0) + { + buf_clear(&buf); + buf_printf(&buf, "%s.%02d", outfile, i); + n = buf_data(&buf); + } + + LOG(LOG_BRIEF, (" Writing \"%s\" as prg, saving from $%04X to $%04X, " + "entry1 at $%04X.\n", n, start, cookedend, entry1)); + write_file(n, &mem); + + buf_free(&mem); + } + + buf_clear(&buf); + perf_buf_print(&perf, &buf); + LOG(LOG_NORMAL, ("All files:\n%s", (char*)buf_data(&buf))); + + buf_clear(&buf); + perf_sort_size_cycles(&perf, 1); + perf_buf_print(&perf, &buf); + LOG(LOG_NORMAL, ("\nSorted on size:\n%s", (char*)buf_data(&buf))); + + buf_clear(&buf); + perf_sort_cycles_size(&perf, 1); + perf_buf_print(&perf, &buf); + LOG(LOG_NORMAL, ("\nSorted on cycles:\n%s", (char*)buf_data(&buf))); + + buf_free(&buf); + perf_free(&perf); +} + +int +main(int argc, char *argv[]) +{ + const char *appl; + + /* init logging */ + LOG_INIT_CONSOLE(LOG_NORMAL); + + appl = fixup_appl(argv[0]); + if(argc < 2) + { + /* missing required command */ + LOG(LOG_ERROR, ("Error: required command is missing.\n")); + print_command_usage(appl, LOG_ERROR); + exit(1); + } + ++argv; + --argc; + if(strcmp(argv[0], "level") == 0) + { + level(appl, argc, argv); + } + else if(strcmp(argv[0], "mem") == 0) + { + mem(appl, argc, argv); + } + else if(strcmp(argv[0], "sfx") == 0) + { + sfx(appl, argc, argv); + } + else if(strcmp(argv[0], "raw") == 0) + { + raw(appl, argc, argv); + } + else if(strcmp(argv[0], "desfx") == 0) + { + desfx(appl, argc, argv); + } + else if(strcmp(argv[0], "-v") == 0) + { + print_license(); + } + else if(strcmp(argv[0], "-?") == 0) + { + print_command_usage(appl, LOG_NORMAL); + } + else + { + /* unknown command */ + LOG(LOG_ERROR, + ("Error: unrecognised command \"%s\".\n", argv[0])); + print_command_usage(appl, LOG_ERROR); + exit(1); + } + + LOG_FREE; + + return 0; +} diff --git a/loader/tools/exomizer-3.1/src/exo_raw.c b/loader/tools/exomizer-3.1/src/exo_raw.c new file mode 100644 index 0000000..16fedff --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_raw.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include +#include +#include "log.h" +#include "getflag.h" +#include "buf.h" +#include "buf_io.h" +#include "exo_helper.h" +#include "exo_util.h" + +static +void print_usage(const char *appl, enum log_level level, + const char *default_out_name) +{ + LOG(level, ("usage: %s [option]... infile\n", appl)); + LOG(level, + (" -b crunch/decrunch backwards\n" + " -r write outfile in reverse order\n" + " -d decrunch (instead of crunch)\n")); + print_crunch_flags(level, default_out_name); +} + +#define DEFAULT_OUTFILE "a.out" + +int +main(int argc, char *argv[]) +{ + char flags_arr[64]; + int decrunch_mode = 0; + int c, infilec; + char **infilev; + + struct crunch_options options = CRUNCH_OPTIONS_DEFAULT; + struct common_flags flags = {NULL, DEFAULT_OUTFILE}; + + struct buf inbuf; + struct buf outbuf; + + const char *appl = fixup_appl(argv[0]); + + flags.options = &options; + options.direction_forward = 1; + + /* init logging */ + LOG_INIT_CONSOLE(LOG_NORMAL); + + LOG(LOG_DUMP, ("flagind %d\n", flagind)); + sprintf(flags_arr, "bdr%s", CRUNCH_FLAGS); + while ((c = getflag(argc, argv, flags_arr)) != -1) + { + LOG(LOG_DUMP, (" flagind %d flagopt '%c'\n", flagind, c)); + switch (c) + { + case 'b': + options.direction_forward = 0; + break; + case 'r': + options.write_reverse = 1; + break; + case 'd': + decrunch_mode = 1; + break; + default: + handle_crunch_flags(c, flagarg, print_usage, appl, &flags); + } + } + + infilev = argv + flagind; + infilec = argc - flagind; + + if (infilec != 1) + { + LOG(LOG_ERROR, ("Error: exactly one input file must be given.\n")); + print_usage(appl, LOG_NORMAL, DEFAULT_OUTFILE); + exit(1); + } + + buf_init(&inbuf); + buf_init(&outbuf); + + read_file(infilev[0], &inbuf); + + if(decrunch_mode) + { + struct decrunch_options dopts; + dopts.imported_encoding = NULL; + dopts.direction_forward = options.direction_forward; + dopts.write_reverse = options.write_reverse; + dopts.flags_proto = options.flags_proto; + + if (dopts.direction_forward == 0) + { + LOG(LOG_NORMAL, ("Decrunching infile \"%s\" to outfile \"%s\" " + " backwards.\n", infilev[0], flags.outfile)); + } + else + { + LOG(LOG_NORMAL, ("Decrunching infile \"%s\" to outfile \"%s\".\n", + infilev[0], flags.outfile)); + } + + decrunch(LOG_NORMAL, &inbuf, 0, &outbuf, &dopts); + } + else + { + struct crunch_info info = STATIC_CRUNCH_INFO_INIT; + if(options.direction_forward == 0) + { + LOG(LOG_NORMAL, ("Crunching infile \"%s\" to outfile \"%s\" " + "backwards.\n", infilev[0], flags.outfile)); + } + else + { + LOG(LOG_NORMAL, ("Crunching infile \"%s\" to outfile \"%s\".\n", + infilev[0], flags.outfile)); + } + crunch(&inbuf, 0, NULL, &outbuf, &options, &info); + + print_crunch_info(LOG_NORMAL, &info); + } + + if(options.write_reverse) + { + reverse_buffer(buf_data(&outbuf), buf_size(&outbuf)); + } + + write_file(flags.outfile, &outbuf); + + buf_free(&outbuf); + buf_free(&inbuf); + + LOG_FREE; + + return 0; +} diff --git a/loader/tools/exomizer-3.1/src/exo_util.c b/loader/tools/exomizer-3.1/src/exo_util.c new file mode 100644 index 0000000..25f594a --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_util.c @@ -0,0 +1,1011 @@ +/* + * Copyright (c) 2008 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "exo_util.h" +#include "log.h" +#include "vec.h" +#include +#include +#include + +#define ATARI_XEX_MAGIC_LEN 2 +#define ORIC_TAP_MAGIC_LEN 3 +#define APPLESINGLE_MAGIC_LEN 8 + +static unsigned char atari_xex_magic[] = {0xff, 0xff}; +static unsigned char oric_tap_magic[] = {0x16, 0x16, 0x16}; +static unsigned char applesingle_magic[] = {0, 5, 0x16, 0, 0, 2, 0, 0}; + +#define OPEN_UNPROVIDED INT_MIN +#define OPEN_DEFAULT (INT_MIN + 1) +struct open_info +{ + int is_raw; + int load_addr; + int offset; + int length; +}; + +int find_sys(const unsigned char *buf, int sys_token, int *stub_lenp) +{ + int outstart = -1; + int state = 1; + + /* skip link and line number */ + int i = 4; + /* exit loop at line end */ + while(i < 1000 && buf[i] != '\0') + { + unsigned char *sys_end; + int c = buf[i]; + switch(state) + { + /* look for and consume sys token */ + case 1: + if((sys_token == -1 && + (c == 0x9e /* cbm */ || + c == 0x8c /* apple 2*/ || + c == 0xbf /* oric 1*/)) || + c == sys_token) + { + state = 2; + } + break; + /* skip spaces and left parenthesis, if any */ + case 2: + if(strchr(" (", c) != NULL) break; + state = 3; + /* convert string number to int */ + case 3: + outstart = strtol((char*)(buf + i), (void*)&sys_end, 10); + if((buf + i) == sys_end) + { + /* we got nothing */ + outstart = -1; + } + state = 4; + break; + case 4: + break; + } + ++i; + } + if (stub_lenp != NULL) + { + /* skip zero byte + next zero link word */ + *stub_lenp = i + 3; + } + + LOG(LOG_DEBUG, ("state when leaving: %d.\n", state)); + return outstart; +} + +static int get_byte(FILE *in) +{ + int byte = fgetc(in); + if(byte == EOF) + { + LOG(LOG_ERROR, ("Error: unexpected end of file.")); + fclose(in); + exit(1); + } + return byte; +} + +static int get_le_word(FILE *in) +{ + int word = get_byte(in); + word |= get_byte(in) << 8; + return word; +} + +static int get_be_word(FILE *in) +{ + int word = get_byte(in) << 8; + word |= get_byte(in); + return word; +} + +static unsigned int get_be_dword(FILE *in) +{ + int word = get_byte(in) << 24; + word |= get_byte(in) << 16; + word |= get_byte(in) << 8; + word |= get_byte(in); + return word; +} + +/** + * Normalizes and validates given offsets and lengths and seeks to the + * normalized offset. Offsets are relative current position of the in file + * unless they are negative, then they are relative to eof of in. + * after searching to the resulting offset the length is normalised. + * if it is negative it is added to the remaining length of the file. + */ +static void seek_and_normalize_offset_and_len(FILE *in, struct open_info *info) +{ + int file_pos; + int file_len; + int offset; + int remaining; + int length; + + file_pos = ftell(in); + /* get the real length of the file and validate the offset*/ + if(fseek(in, 0, SEEK_END)) + { + LOG(LOG_ERROR, ("Error: can't seek to EOF.\n")); + fclose(in); + exit(1); + } + file_len = ftell(in); + offset = info->offset; + if (offset == OPEN_UNPROVIDED || offset == OPEN_DEFAULT) + { + offset = 0; + } + if (offset < 0) + { + offset += file_len; + } + else + { + offset += file_pos; + } + if (offset < file_pos || offset > file_len) + { + /* bad offset */ + LOG(LOG_ERROR, ("Error: offset %d (%d) out of range.\n", + info->offset, offset)); + fclose(in); + exit(1); + } + if(fseek(in, offset, SEEK_SET)) + { + LOG(LOG_ERROR, ("Error: seek to offset %d (%d) failed.\n", + info->offset, offset)); + fclose(in); + exit(1); + } + remaining = file_len - offset; + length = info->length; + if (length == OPEN_UNPROVIDED || length == OPEN_DEFAULT) + { + length = remaining;; + } + if (length < 0) + { + length += remaining;; + } + if (length < 0 || length > remaining) + { + /* bad offset */ + LOG(LOG_ERROR, ("Error: length %d (%d) out of range.\n", + info->length, length)); + fclose(in); + exit(1); + } + + info->offset = offset; + info->length = length; +} + +/** + * Opens the given file and sets the fields in the open_info according to what + * is peovided by the file name suffix. If any of the fields load_addr, offset + * or length is unprovided or defaulted by the file name suffix then it will be + * set to OPEN_UNPROVIDED or OPEN_DEFAULT. + * if the file name suffix indicates that it is a raw file (@) then the + * field is_raw will be set to 1. + */ +static +FILE * +open_file(const char *name, struct open_info *open_info) +{ + FILE *in; + int is_raw = 0; + int tries; + char *tries_arr[3]; + int load = OPEN_UNPROVIDED; + int offset = OPEN_UNPROVIDED; + int length = OPEN_UNPROVIDED; + + for (tries = 0;; ++tries) + { + char *load_str; + char *at_str; + + in = fopen(name, "rb"); + if (in != NULL || is_raw == 1 || tries == 3) + { + /* We have succeded in opening the file. + * There's no address suffix. */ + break; + } + + /* hmm, let's see if the user is trying to relocate it */ + load_str = strrchr(name, ','); + at_str = strrchr(name, '@'); + if(at_str != NULL && (load_str == NULL || at_str > load_str)) + { + is_raw = 1; + load_str = at_str; + } + + if (load_str == NULL) + { + /* nope, */ + break; + } + + *load_str = '\0'; + ++load_str; + + /* relocation was requested */ + tries_arr[tries] = load_str; + } + if (in == NULL) + { + LOG(LOG_ERROR, + (" can't open file \"%s\" for input\n", name)); + exit(1); + } + + if (--tries >= 0) + { + char *p = tries_arr[tries]; + load = OPEN_DEFAULT; + if (p[0] != '\0' && str_to_int(p, &load, NULL) != 0) + { + /* we fail */ + LOG(LOG_ERROR, (" can't parse load address from \"%s\"\n", p)); + exit(1); + } + } + if (--tries >= 0) + { + char *p = tries_arr[tries]; + offset = OPEN_DEFAULT; + if (p[0] != '\0' && str_to_int(p, &offset, NULL) != 0) + { + /* we fail */ + LOG(LOG_ERROR, (" can't parse offset from \"%s\"\n", p)); + exit(1); + } + } + if (--tries >= 0) + { + char *p = tries_arr[tries]; + length = OPEN_DEFAULT; + if (p[0] != '\0') + { + if (str_to_int(p, &length, NULL) != 0) + { + /* we fail */ + LOG(LOG_ERROR, (" can't parse length from \"%s\"\n", p)); + exit(1); + } + } + } + + if (open_info != NULL) + { + open_info->is_raw = is_raw; + open_info->load_addr = load; + open_info->offset = offset; + open_info->length = length; + } + return in; +} + +static int is_matching_header(unsigned char *buf1, + unsigned char *buf2, + int len) +{ + int i; + for (i = 0; i < len; ++i) + { + if (buf1[i] != buf2[i]) + { + return 0; + } + } + return 1; +} + +/** + * Detects the file type by reading a few bytes at the start and then + * check for byte patterns. Won't detect RAW, default to PRG. + */ +static enum file_type detect_type(FILE *in) +{ + /* read the beginning of the file */ + unsigned char buf[8] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; + /* Default to PRG */ + enum file_type type = PRG; + + if (fread(buf, 1, 8, in) != 8 && + ferror(in)) + { + LOG(LOG_ERROR, ("Error: failed to read from file.\n")); + fclose(in); + exit(1); + } + if(fseek(in, 0, SEEK_SET)) + { + LOG(LOG_ERROR, ("Error: can't seek to file start.\n")); + fclose(in); + exit(1); + } + + if(is_matching_header(buf, applesingle_magic, APPLESINGLE_MAGIC_LEN)) + { + type = APPLESINGLE; + } + else if(is_matching_header(buf, atari_xex_magic, ATARI_XEX_MAGIC_LEN)) + { + type = ATARI_XEX; + } + else if(is_matching_header(buf, oric_tap_magic, ORIC_TAP_MAGIC_LEN)) + { + type = ORIC_TAP; + } + return type; +} + +/* Handles xex files */ +static void load_xex(unsigned char mem[65536], FILE *in, + struct load_info *info) +{ + int run = -1; + int jsr = -1; + int min = 65536, max = 0; + unsigned char buf[ATARI_XEX_MAGIC_LEN]; + + if (fread(buf, 1, ATARI_XEX_MAGIC_LEN, in) != ATARI_XEX_MAGIC_LEN) + { + LOG(LOG_ERROR, + ("Error: failed to read Atari XEX header from file.\n")); + fclose(in); + exit(1); + } + if (!is_matching_header(buf, atari_xex_magic, ATARI_XEX_MAGIC_LEN)) + { + LOG(LOG_ERROR, ("Error: not a valid Atari xex-header.")); + fclose(in); + exit(1); + } + + goto initial_state; + for(;;) + { + int start, end, len; + + start = fgetc(in); + if(start == EOF) break; + ungetc(start, in); + + start = get_le_word(in); + if(start == 0xffff) + { + initial_state: + /* allowed optional header */ + start = get_le_word(in); + } + end = get_le_word(in); + if(start > 0xffff || end > 0xffff || end < start) + { + LOG(LOG_ERROR, ("Error: corrupt data in xex-file.")); + fclose(in); + exit(1); + } + if(start == 0x2e2 && end == 0x2e3) + { + /* init vector */ + jsr = get_le_word(in); + LOG(LOG_VERBOSE, ("Found xex initad $%04X.\n", jsr)); + continue; + } + if(start == 0x2e0 && end == 0x2e1) + { + /* run vector */ + run = get_le_word(in); + LOG(LOG_VERBOSE, ("Found xex runad $%04X.\n", run)); + continue; + } + ++end; + jsr = -1; + if(start < min) min = start; + if(end > max) max = end; + + len = fread(mem + start, 1, end - start, in); + if(len != end - start) + { + LOG(LOG_ERROR, ("Error: unexpected end of xex-file.\n")); + fclose(in); + exit(1); + } + LOG(LOG_VERBOSE, (" xex chunk loading from $%04X to $%04X\n", + start, end)); + } + + if(run == -1 && jsr != -1) run = jsr; + + info->start = min; + info->end = max; + info->basic_var_start = -1; + info->run = -1; + if(run != -1) + { + info->run = run; + } +} + +/* Handles tap files */ +static void load_oric_tap(unsigned char mem[65536], FILE *in, + struct load_info *load_info) +{ + int c; + int autostart; + int start, end, len; + unsigned char buf[ORIC_TAP_MAGIC_LEN]; + + /* read oric tap header */ + if (fread(buf, 1, ORIC_TAP_MAGIC_LEN, in) != ORIC_TAP_MAGIC_LEN) + { + LOG(LOG_ERROR, ("Error: failed to read Oric TAP header from file.\n")); + fclose(in); + exit(1); + } + if (!is_matching_header(buf, oric_tap_magic, ORIC_TAP_MAGIC_LEN)) + { + LOG(LOG_ERROR, ("Error: not a valid Oric tap-header.")); + fclose(in); + exit(1); + } + + /* There can be optionally more 0x16 bytes */ + while((c = get_byte(in)) == 0x16); + + /* next byte must be 0x24 */ + if(c != 0x24) + { + LOG(LOG_ERROR, ("Error: bad sync byte after lead-in in Oric tap-file " + "header, got $%02X but expected $24\n", c)); + fclose(in); + exit(1); + } + + /* now we are in sync, lets be lenient */ + get_byte(in); /* should be 0x0 */ + get_byte(in); /* should be 0x0 */ + get_byte(in); /* should be 0x0 or 0x80 */ + autostart = (get_byte(in) != 0); /* should be 0x0, 0x80 or 0xc7 */ + end = get_be_word(in) + 1; /* the header end address is inclusive */ + start = get_be_word(in); + get_byte(in); /* should be 0x0 */ + /* read optional file name */ + while(get_byte(in) != 0x0); + + /* read the data */ + len = fread(mem + start, 1, end - start, in); + if(len != end - start) + { + LOG(LOG_BRIEF, ("Warning: Oric tap-file contains %d byte(s) data " + "less than expected.\n", end - start - len)); + end = start + len; + } + LOG(LOG_VERBOSE, (" Oric tap-file loading from $%04X to $%04X\n", + start, end)); + + /* fill in the fields */ + if (load_info != NULL) + { + load_info->start = start; + load_info->end = end; + load_info->run = -1; + load_info->basic_var_start = -1; + if(autostart) + { + load_info->run = start; + } + if (load_info->basic_txt_start >= start && + load_info->basic_txt_start < end) + { + load_info->basic_var_start = end - 1; + } + } +} + +struct as_descr +{ + unsigned int id; + unsigned int offset; + unsigned int length; +}; + +static int cb_cmp_as_descr_id(const void *a, const void *b) +{ + const struct as_descr *ap = a; + const struct as_descr *bp = b; + + int result = 0; + if (ap->id < bp->id) + { + result = -1; + } + else if (ap->id > bp->id) + { + result = 1; + } + return result; +} + +/** + * Gets the load address from a PRODOS entry in an AppleSingle file. + * Also validates that the file type is binary or Applesoft basic. + */ +static int load_applesingle_prodos_entry(FILE *in, + struct as_descr *prodos, + int *file_type) +{ + int type; + int load_addr; + if (prodos->length != 8) + { + LOG(LOG_ERROR, ("Error: invalid length of prodos entry %d.\n", + prodos->length)); + fclose(in); + exit(1); + } + if (fseek(in, prodos->offset, SEEK_SET) != 0) + { + LOG(LOG_ERROR, + ("Error: failed to seek to prodos entry at offset %d.\n", + prodos->offset)); + fclose(in); + exit(1); + } + get_be_word(in); /* access */ + type = get_be_word(in); /* file type */ + if (type == 0xff) + { + /* system file that loads to $2000 */ + load_addr = 0x2000; + } + else + if (type == 6 || type == 0xfc) + { + /* binary, Applesoft basic */ + load_addr = get_be_dword(in); /* aux file type */ + } + else + { + /* unsupported file type*/ + LOG(LOG_ERROR, + ("Error: unsupported value $%X for PRODOS filetype.\n", type)); + fclose(in); + exit(1); + } + if (load_addr < 0 || load_addr > 0xffff) + { + /* not binary, nor Applesoft basic */ + LOG(LOG_ERROR, + ("Error: unexpected value $%X for PRODOS aux filetype.\n", + type)); + fclose(in); + exit(1); + } + if (file_type != NULL) + { + *file_type = type; + } + return load_addr; +} + +/* Handles apple single files */ +static void load_applesingle(unsigned char mem[65536], FILE *in, + struct load_info *load_info) +{ + int desc_size; + int i; + unsigned char buf[16]; + struct vec descrs; + struct as_descr *prodosp; + struct as_descr *datap; + struct as_descr key; + int load_addr; + int file_type; + int length; + + /* read oric tap header */ + if (fread(buf, 1, APPLESINGLE_MAGIC_LEN, in) != APPLESINGLE_MAGIC_LEN) + { + LOG(LOG_ERROR, + ("Error: failed to read applesingle header from file.\n")); + fclose(in); + exit(1); + } + if (!is_matching_header(buf, applesingle_magic, APPLESINGLE_MAGIC_LEN)) + { + LOG(LOG_ERROR, ("Error: not a valid AppleSingle-header.")); + fclose(in); + exit(1); + } + + /* skip filler */ + if (fread(buf, 1, 16, in) != 16) + { + LOG(LOG_ERROR, ("Error: unexpected end of AppleSingle file.")); + fclose(in); + exit(1); + } + + /* Read the number of entr descriptors that follow */ + desc_size = get_be_word(in); + + vec_init(&descrs, sizeof(struct as_descr)); + + for (i = 0; i < desc_size; ++i) + { + struct as_descr descr; + descr.id = get_be_dword(in); + descr.offset = get_be_dword(in); + descr.length = get_be_dword(in); + if (!vec_insert_uniq(&descrs, cb_cmp_as_descr_id, &descr, NULL)) + { + LOG(LOG_ERROR, ("Error: duplicate descriptor for id %d\n.", + descr.id)); + fclose(in); + exit(1); + } + } + + /* find the PRODOS descriptor */ + key.id = 11; + prodosp = vec_find2(&descrs, cb_cmp_as_descr_id, &key); + if (prodosp == NULL) + { + LOG(LOG_ERROR, + ("Error: unable to find descr 11 in AppleSingle file\n.")); + fclose(in); + exit(1); + } + + /* find the data descriptor */ + key.id = 1; + datap = vec_find2(&descrs, cb_cmp_as_descr_id, &key); + if (datap == NULL) + { + LOG(LOG_ERROR, + ("Error: unable to find descr 1 in AppleSingle file\n.")); + fclose(in); + exit(1); + } + + load_addr = load_applesingle_prodos_entry(in, prodosp, &file_type); + + if (fseek(in, datap->offset, SEEK_SET) != 0) + { + LOG(LOG_ERROR, + ("Error: failed to seek to data entry at offset %d.\n", + prodosp->offset)); + fclose(in); + exit(1); + } + length = datap->length; + if (length > 65536 - load_addr) + { + /* truncating length to available buffer space */ + length = 65536 - load_addr; + } + if (fread(mem + load_addr, 1, datap->length, in) != datap->length) + { + LOG(LOG_ERROR, ("Error: unexpected end of AppleSingle file.")); + fclose(in); + exit(1); + } + + if (load_info != NULL) + { + load_info->start = load_addr; + load_info->end = load_addr + datap->length; + load_info->run = -1; + load_info->basic_var_start = -1; + if (file_type != 0xfc) + { + /* We're not a basic file */ + load_info->run = load_addr; + } + if (file_type == 0xff) + { + /* We're a system file, not plain bin or basic */ + load_info->type = APPLESINGLE_SYS; + } + if (load_info->basic_txt_start >= load_info->start && + load_info->basic_txt_start < load_info->end) + { + load_info->basic_var_start = load_info->end; + } + } + + vec_free(&descrs, NULL); +} + +/** + * Requires that open_info->load_addr is set to a proper value + * (not OPEN_UNPROVIDED nor OPEN_DEFAULT). + */ +static void load_raw(unsigned char mem[65536], FILE *in, + struct open_info *open_info, + struct load_info *load_info) +{ + int len; + + if (open_info->load_addr == OPEN_UNPROVIDED || + open_info->load_addr == OPEN_DEFAULT) + { + LOG(LOG_ERROR, + ("Error: No load address given for raw file.")); + fclose(in); + exit(1); + } + + seek_and_normalize_offset_and_len(in, open_info); + len = fread(mem + open_info->load_addr, 1, open_info->length, in); + + if (load_info != NULL) + { + load_info->start = open_info->load_addr; + load_info->end = open_info->load_addr + len; + load_info->basic_var_start = -1; + load_info->run = -1; + if(load_info->basic_txt_start >= load_info->start && + load_info->basic_txt_start < load_info->end) + { + load_info->basic_var_start = load_info->end; + } + } +} + +static int try_load_bbc_inf(unsigned char mem[65536], + FILE *in, + const char *filename, + struct open_info *open_info, + struct load_info *load_info) +{ + FILE *inf; + char *p; + unsigned int load; + unsigned int run; + + /* try to open shadowing .inf file */ + struct buf name_buf = STATIC_BUF_INIT; + buf_printf(&name_buf, "%s.inf", filename); + p = buf_data(&name_buf); + inf = fopen(p, "rb"); + if (inf == NULL) + { + /* failed to open */ + return 0; + } + /* parse .inf file here and populate load_info */ + if (fscanf(inf, "%*s %x %x", &load, &run) != 2) + { + LOG(LOG_ERROR, + ("Error: Failed to parse BBCIm/BBCXfer inf from \"%s\".", p)); + fclose(inf); + fclose(in); + exit(1); + } + fclose(inf); + load &= 0xffff; + run &= 0xffff; + LOG(LOG_DEBUG, ("BBC inf: load %06X, run %06X.", load, run)); + + /* read data file here */ + open_info->load_addr = load; + load_raw(mem, in, open_info, load_info); + if (load_info != NULL) + { + /* set run address after load_raw since it clears it */ + load_info->run = run; + } + + /* success */ + buf_free(&name_buf); + return 1; +} + +void load_located(const char *filename, unsigned char mem[65536], + struct load_info *info) +{ + struct open_info open_info; + FILE *in; + enum file_type type; + int load_addr; + + in = open_file(filename, &open_info); + if (open_info.is_raw) + { + type = RAW; + } + else if (open_info.load_addr != OPEN_UNPROVIDED) + { + /* relocaded prg */ + type = PRG; + } + else if (try_load_bbc_inf(mem, in, filename, &open_info, info)) + { + type = BBC_INF; + } + else + { + type = detect_type(in); + } + if (info != NULL) + { + info->type = type; + } + switch (type) + { + case ATARI_XEX: + /* file is an xex file */ + load_xex(mem, in, info); + break; + case ORIC_TAP: + /* file is an oric tap file */ + load_oric_tap(mem, in, info); + break; + case APPLESINGLE: + /* file is an AppleSingle file */ + load_applesingle(mem, in, info); + break; + case BBC_INF: + /* already loaded by try_load_bbc_inf(...) */ + break; + case PRG: + load_addr = get_le_word(in); + if (open_info.load_addr == OPEN_UNPROVIDED || + open_info.load_addr == OPEN_DEFAULT) + { + open_info.load_addr = load_addr; + } + /* no break on purpose */ + case RAW: + if (open_info.load_addr == OPEN_UNPROVIDED || + open_info.load_addr == OPEN_DEFAULT) + { + LOG(LOG_ERROR, + ("Error: No load address given for raw file \"%s\".\n", + filename)); + fclose(in); + exit(1); + } + + /* file is a located raw file or a prg file + * with it's header already read */ + load_raw(mem, in, &open_info, info); + break; + default: + LOG(LOG_ERROR, ("Error: unknown file type for file \"%s\".\n", + filename)); + fclose(in); + exit(1); + break; + } + fclose(in); + + LOG(LOG_BRIEF, + (" Reading \"%s\", loading from $%04X to $%04X.\n", + filename, info->start, info->end)); +} + +int str_to_int(const char *str, int *value, const char **strp) +{ + int status = 0; + do { + char *str_end; + long lval; + + /* base 0 is auto detect */ + int base = 0; + + if (*str == '\0') + { + /* no string to parse */ + status = 1; + break; + } + + if (*str == '$' || *str == '&') + { + /* a $ or & prefix specifies base 16 */ + ++str; + base = 16; + } + if (*str == '%') + { + /* a % prefix specifies base 2 */ + ++str; + base = 2; + } + + lval = strtol(str, &str_end, base); + if (str == str_end) + { + /* found nothing to parse */ + status = 1; + break; + } + + if(strp == NULL && *str_end != '\0') + { + /* there is garbage in the string */ + status = 1; + break; + } + + if(value != NULL) + { + /* all is well, set the out parameter */ + *value = (int)lval; + } + + if(strp != NULL) + { + *strp = str_end; + } + + } while(0); + + return status; +} + +const char *fixup_appl(char *appl) +{ + char *applp; + + /* strip pathprefix from appl */ + applp = strrchr(appl, '\\'); + if (applp != NULL) + { + appl = applp + 1; + } + applp = strrchr(appl, '/'); + if (applp != NULL) + { + appl = applp + 1; + } + /* strip possible exe suffix */ + applp = appl + strlen(appl) - 4; + if(strcmp(applp, ".exe") == 0 || strcmp(applp, ".EXE") == 0) + { + *applp = '\0'; + } + return appl; +} diff --git a/loader/tools/exomizer-3.1/src/exo_util.h b/loader/tools/exomizer-3.1/src/exo_util.h new file mode 100644 index 0000000..fe28328 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exo_util.h @@ -0,0 +1,75 @@ +#ifndef EXO_UTIL_ALREADY_INCLUDED +#define EXO_UTIL_ALREADY_INCLUDED +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2008 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "log.h" +#include "buf.h" + +/* + * target is the basic token for the sys/call basic command + * it may be -1 for hardcoded detection of a few targets. + */ +int find_sys(const unsigned char *buf, int target, int *stub_lenp); + +enum file_type {UNKNOWN, RAW, ATARI_XEX, ORIC_TAP, APPLESINGLE, + APPLESINGLE_SYS, BBC_INF, PRG}; +struct load_info +{ + int basic_txt_start; /* in */ + int basic_var_start; /* out */ + int run; /* out */ + int start; /* out */ + int end; /* out */ + enum file_type type; /* out */ +}; + +void load_located(const char *filename, unsigned char mem[65536], + struct load_info *info); + +/* + * returns 0 if the conversion was successful, 1 otherwise. + * + * if strp == NULL then str must be zero-terminated directly after the + * value for the conversion to be considered successful. + * + * if strp != NULL then on an successful conversion it will be set to + * the character immediately following the last charachter in the + * converted integer. + */ +int str_to_int(const char *str, int *value, const char **strp); + +const char *fixup_appl(char *appl); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/exodec.c b/loader/tools/exomizer-3.1/src/exodec.c new file mode 100644 index 0000000..0c4e1e0 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exodec.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + */ +#include +#include +#include +#include "exodec.h" +#include "log.h" + +static int bitbuf_rotate(struct dec_ctx *ctx, int carry) +{ + int carry_out; + if (ctx->flags_proto & PFLAG_BITS_ORDER_BE) + { + /* rol (new) */ + carry_out = (ctx->bitbuf & 0x80) != 0; + ctx->bitbuf <<= 1; + if (carry) + { + ctx->bitbuf |= 0x01; + } + } + else + { + /* ror (old) */ + carry_out = ctx->bitbuf & 0x01; + ctx->bitbuf >>= 1; + if (carry) + { + ctx->bitbuf |= 0x80; + } + } + return carry_out; +} + +static char *get(struct buf *buf) +{ + return buf_data(buf); +} + +static int +get_byte(struct dec_ctx *ctx) +{ + int c; + if(ctx->inpos == ctx->inend) + { + LOG(LOG_ERROR, ("unexpected end of input data\n")); + exit(1); + } + c = ctx->inbuf[ctx->inpos++]; + ctx->bits_read += 8; + + return c; +} + +static int +get_bits(struct dec_ctx *ctx, int count) +{ + int byte_copy = 0; + int val; + + val = 0; + if (ctx->flags_proto & PFLAG_BITS_COPY_GT_7) + { + while (count > 7) + { + byte_copy = count >> 3; + count &= 7; + } + } + + /*printf("get_bits: count = %d", count);*/ + while(count-- > 0) + { + int carry = bitbuf_rotate(ctx, 0); + if (ctx->bitbuf == 0) + { + ctx->bitbuf = get_byte(ctx); + ctx->bits_read -= 8; + carry = bitbuf_rotate(ctx, 1); + } + val <<= 1; + val |= carry; + + /*printf("bit read %d\n", val &1);*/ + ctx->bits_read++; + } + /*printf(" val = %d\n", val);*/ + + while (byte_copy-- > 0) + { + val <<= 8; + val |= get_byte(ctx); + } + + return val; +} + +static int +get_gamma_code(struct dec_ctx *ctx) +{ + int gamma_code; + /* get bitnum index */ + gamma_code = 0; + while(get_bits(ctx, 1) == 0) + { + ++gamma_code; + } + return gamma_code; +} + +static int +get_cooked_code_phase2(struct dec_ctx *ctx, int index) +{ + int base; + struct dec_table *tp; + tp = &ctx->t; + + base = tp->table_lo[index] | (tp->table_hi[index] << 8); + return base + get_bits(ctx, tp->table_bi[index]); +} + +static +void +table_init(struct dec_ctx *ctx, struct dec_table *tp) /* IN/OUT */ +{ + int i, end; + unsigned int a = 0; + unsigned int b = 0; + + tp->table_bit[0] = 2; + tp->table_bit[1] = 4; + tp->table_bit[2] = 4; + if (ctx->flags_proto & PFLAG_4_OFFSET_TABLES) + { + end = 68; + tp->table_bit[3] = 4; + + tp->table_off[0] = 64; + tp->table_off[1] = 48; + tp->table_off[2] = 32; + tp->table_off[3] = 16; + } + else + { + end = 52; + tp->table_off[0] = 48; + tp->table_off[1] = 32; + tp->table_off[2] = 16; + } + + for(i = 0; i < end; ++i) + { + if(i & 0xF) + { + a += 1 << b; + } else + { + a = 1; + } + + tp->table_lo[i] = a & 0xFF; + tp->table_hi[i] = a >> 8; + + if (ctx->flags_proto & PFLAG_BITS_COPY_GT_7) + { + b = get_bits(ctx, 3); + b |= get_bits(ctx, 1) << 3; + } + else + { + b = get_bits(ctx, 4); + } + tp->table_bi[i] = b; + } +} + +void +dec_ctx_init(struct dec_ctx *ctx, + struct buf *enc_in, /* optional */ + struct buf *inbuf, struct buf *outbuf, int flags_proto) +{ + struct buf *enc = enc_in; + + if (enc_in == NULL) + { + enc = inbuf; + } + ctx->bits_read = 0; + ctx->inbuf = buf_data(enc); + ctx->inend = buf_size(enc); + ctx->inpos = 0; + ctx->flags_proto = flags_proto; + + ctx->outbuf = outbuf; + + if (flags_proto & PFLAG_BITS_ALIGN_START) + { + ctx->bitbuf = 0; + } + else + { + /* init bitbuf */ + ctx->bitbuf = get_byte(ctx); + } + + /* init tables */ + table_init(ctx, &ctx->t); + if (enc_in != NULL) + { + /* switch to inbuf */ + ctx->inbuf = buf_data(inbuf); + ctx->inend = buf_size(inbuf); + ctx->inpos = 0; + + if (flags_proto & PFLAG_BITS_ALIGN_START) + { + ctx->bitbuf = 0; + } + else + { + /* init bitbuf */ + ctx->bitbuf = get_byte(ctx); + } + } +} + +void +dec_ctx_table_dump(struct dec_ctx *ctx, struct buf *enc_out) +{ + if (enc_out != NULL) + { + int i, j, offset_tables= 3; + if (ctx->flags_proto & PFLAG_4_OFFSET_TABLES) + { + offset_tables = 4; + } + + buf_clear(enc_out); + for(i = 0; i < 16; ++i) + { + buf_printf(enc_out, "%X", ctx->t.table_bi[i]); + } + for(j = 0; j < offset_tables; ++j) + { + int start; + int end; + buf_append_char(enc_out, ','); + start = ctx->t.table_off[j]; + end = start + (1 << ctx->t.table_bit[j]); + for(i = start; i < end; ++i) + { + buf_printf(enc_out, "%X", ctx->t.table_bi[i]); + } + } + } +} + +void dec_ctx_free(struct dec_ctx *ctx) +{ +} + +void dec_ctx_decrunch(struct dec_ctx *ctx) +{ + int bits = ctx->bits_read; + int val; + int i; + int len; + int literal = 1; + int offset = 0; + int src = 0; + int treshold = (ctx->flags_proto & PFLAG_4_OFFSET_TABLES)? 4: 3; + int reuse_offset_state = 1; + + if (ctx->flags_proto & PFLAG_IMPL_1LITERAL) + { + goto literal_start; + } + + for(;;) + { + int reuse_offset; + reuse_offset_state <<= 1; + reuse_offset_state |= literal; + + literal = 0; + bits = ctx->bits_read; + LOG(LOG_DEBUG, ("[%02X]",ctx->bitbuf)); + if(get_bits(ctx, 1)) + { + literal_start: + /* literal */ + len = 1; + + LOG(LOG_DEBUG, ("[%d] literal $%02X\n", + buf_size(ctx->outbuf), + ctx->inbuf[ctx->inpos])); + + literal = 1; + + goto literal; + } + + val = get_gamma_code(ctx); + if(val == 16) + { + /* done */ + break; + } + if(val == 17) + { + len = get_bits(ctx, 16); + literal = 1; + + LOG(LOG_DEBUG, ("[%d] literal copy len %d\n", + buf_size(ctx->outbuf), len)); + + goto literal; + } + + len = get_cooked_code_phase2(ctx, val); + + reuse_offset = 0; + if ((ctx->flags_proto & PFLAG_REUSE_OFFSET) != 0 && + (reuse_offset_state & 3) == 1) + { + /* offset reuse state, read a bit */ + reuse_offset = get_bits(ctx, 1); + LOG(LOG_DEBUG, ("[%d] offset reuse bit = %d, latest = %d\n", + buf_size(ctx->outbuf), reuse_offset, + offset)); + } + if (!reuse_offset) + { + i = (len > treshold ? treshold : len) - 1; + val = ctx->t.table_off[i] + get_bits(ctx, ctx->t.table_bit[i]); + offset = get_cooked_code_phase2(ctx, val); + } + LOG(LOG_DEBUG, ("[%d] sequence offset = %d, len = %d\n", + buf_size(ctx->outbuf), offset, len)); + + src = buf_size(ctx->outbuf) - offset; + + literal: + do { + if(literal) + { + val = get_byte(ctx); + } + else + { + val = get(ctx->outbuf)[src++]; + } + buf_append_char(ctx->outbuf, val); + } while (--len > 0); + + if (ctx->flags_proto & PFLAG_4_OFFSET_TABLES) + { + LOG(LOG_DEBUG, ("bits read for this iteration %d, total %d.\n", + ctx->bits_read - bits, ctx->bits_read - 280)); + } + else + { + LOG(LOG_DEBUG, ("bits read for this iteration %d, total %d.\n", + ctx->bits_read - bits, ctx->bits_read - 216)); + } + } +} diff --git a/loader/tools/exomizer-3.1/src/exodec.h b/loader/tools/exomizer-3.1/src/exodec.h new file mode 100644 index 0000000..56814b7 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/exodec.h @@ -0,0 +1,76 @@ +#ifndef ALREADY_INCLUDED_EXODEC +#define ALREADY_INCLUDED_EXODEC +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "buf.h" +#include "flags.h" + +struct dec_table +{ + unsigned char table_bit[8]; + unsigned char table_off[8]; + unsigned char table_bi[100]; + unsigned char table_lo[100]; + unsigned char table_hi[100]; +}; + +struct dec_ctx +{ + int inpos; + int inend; + unsigned char *inbuf; + struct buf *outbuf; + unsigned char bitbuf; + /* dep_table */ + struct dec_table t; + int bits_read; + int flags_proto; +}; + +/* returns the encoding */ +void +dec_ctx_init(struct dec_ctx *ctx, + struct buf *enc_in, /* optional */ + struct buf *inbuf, struct buf *outbuf, int flags_proto); + +void +dec_ctx_table_dump(struct dec_ctx *ctx, struct buf *enc_out); + +void +dec_ctx_free(struct dec_ctx *ctx); + +void dec_ctx_decrunch(struct dec_ctx *ctx); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/expr.c b/loader/tools/exomizer-3.1/src/expr.c new file mode 100644 index 0000000..870662b --- /dev/null +++ b/loader/tools/exomizer-3.1/src/expr.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "expr.h" +#include "chunkpool.h" +#include "log.h" + +#include + +static struct chunkpool s_expr_pool; + +void expr_init() +{ + chunkpool_init(&s_expr_pool, sizeof(struct expr)); +} + +void expr_free() +{ + chunkpool_free(&s_expr_pool); +} + +void expr_dump(int level, struct expr *e) +{ + switch(e->expr_op) + { + case SYMBOL: + LOG(level, ("expr %p symref %s\n", (void*)e, e->type.symref)); + break; + case NUMBER: + LOG(level, ("expr %p number %d\n", (void*)e, e->type.number)); + break; + case vNEG: + LOG(level, ("expr %p unary op %d, referring to %p\n", + (void*)e, e->expr_op, (void*)e->type.arg1)); + case LNOT: + LOG(level, ("expr %p unary op %d, referring to %p\n", + (void*)e, e->expr_op, (void*)e->type.arg1)); + break; + default: + LOG(level, ("expr %p binary op %d, arg1 %p, arg2 %p\n", + (void*)e, e->expr_op, (void*)e->type.arg1, + (void*)e->expr_arg2)); + + } +} + +struct expr *new_expr_op1(i16 op, struct expr *arg) +{ + struct expr *val; + + if(op != vNEG && op != LNOT) + { + /* error, invalid unary operator */ + LOG(LOG_ERROR, ("%d not allowed as unary operator\n", op)); + exit(1); + } + + val = chunkpool_malloc(&s_expr_pool); + val->expr_op = op; + val->type.arg1 = arg; + + expr_dump(LOG_DEBUG, val); + + return val; +} + +struct expr *new_expr_op2(i16 op, struct expr *arg1, struct expr *arg2) +{ + struct expr *val; + + if(op == vNEG || + op == LNOT || + op == NUMBER || + op == SYMBOL) + { + /* error, invalid binary operator */ + printf("op %d, vNEG %d, NUMBER %d, SYMBOL %d\n", op, vNEG, NUMBER, SYMBOL); + LOG(LOG_ERROR, ("%d not allowed as binary operator\n", op)); + exit(1); + } + + val = chunkpool_malloc(&s_expr_pool); + val->expr_op = op; + val->type.arg1 = arg1; + val->expr_arg2 = arg2; + + expr_dump(LOG_DEBUG, val); + + return val; +} + +struct expr *new_expr_symref(const char *symbol) +{ + struct expr *val; + + val = chunkpool_malloc(&s_expr_pool); + val->expr_op = SYMBOL; + val->type.symref = symbol; + + expr_dump(LOG_DEBUG, val); + + return val; +} + +struct expr *new_expr_number(i32 number) +{ + struct expr *val; + + LOG(LOG_DEBUG, ("creating new number %d\n", number)); + + val = chunkpool_malloc(&s_expr_pool); + val->expr_op = NUMBER; + val->type.number = number; + + expr_dump(LOG_DEBUG, val); + + return val; +} diff --git a/loader/tools/exomizer-3.1/src/expr.h b/loader/tools/exomizer-3.1/src/expr.h new file mode 100644 index 0000000..ca694ba --- /dev/null +++ b/loader/tools/exomizer-3.1/src/expr.h @@ -0,0 +1,63 @@ +#ifndef ALREADY_INCLUDED_EXPR +#define ALREADY_INCLUDED_EXPR +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "int.h" +#include "asm.tab.h" + +union expr_type +{ + i32 number; + const char *symref; + struct expr *arg1; +}; + +struct expr +{ + union expr_type type; + struct expr *expr_arg2; + i16 expr_op; +}; + +void expr_init(void); +void expr_free(void); + +struct expr *new_expr_op1(i16 op, struct expr *arg); +struct expr *new_expr_op2(i16 op, struct expr *arg1, struct expr *arg2); +struct expr *new_expr_symref(const char *symbol); +struct expr *new_expr_number(i32 number); +void expr_dump(int level, struct expr *e); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/flags.h b/loader/tools/exomizer-3.1/src/flags.h new file mode 100644 index 0000000..5ffb716 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/flags.h @@ -0,0 +1,75 @@ +#ifndef INCLUDED_FLAGS +#define INCLUDED_FLAGS +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +/* + * bit 0 Controls bit bit orientation, 1=big endian, 0=little endian + * bit 1 Contols how more than 7 bits are shifted 1=split into a shift of + * of less than 8 bits + a byte (new), 0=all bits are shifted + * bit 2 Implicit first literal byte: 1=enable, 0=disable + * bit 3 Align bit stream towards start without flag: 1=enable, 0=disable + * bit 4 Decides if we are to have two lengths (1 and 2) or three lengths + * (1, 2 and 3) using dedicated decrunch tables: 0=two, 1=three + * bit 5 Decides if we are reusing offsets: 1=enable, 0=disable + */ +#define PBIT_BITS_ORDER_BE 0 +#define PBIT_BITS_COPY_GT_7 1 +#define PBIT_IMPL_1LITERAL 2 +#define PBIT_BITS_ALIGN_START 3 +#define PBIT_4_OFFSET_TABLES 4 +#define PBIT_REUSE_OFFSET 5 + +#define PFLAG_BITS_ORDER_BE (1 << PBIT_BITS_ORDER_BE) +#define PFLAG_BITS_COPY_GT_7 (1 << PBIT_BITS_COPY_GT_7) +#define PFLAG_IMPL_1LITERAL (1 << PBIT_IMPL_1LITERAL) +#define PFLAG_BITS_ALIGN_START (1 << PBIT_BITS_ALIGN_START) +#define PFLAG_4_OFFSET_TABLES (1 << PBIT_4_OFFSET_TABLES) +#define PFLAG_REUSE_OFFSET (1 << PBIT_REUSE_OFFSET) + +/* + * bit 0 Literal sequences + * bit 1 Sequences with length 1 + * bit 2 Sequences with length > 255 where (length & 255) would have been + * using its own decrunch table + */ +#define TBIT_LIT_SEQ 0 +#define TBIT_LEN1_SEQ 1 +#define TBIT_LEN0123_SEQ_MIRRORS 2 + +#define TFLAG_LIT_SEQ (1 << TBIT_LIT_SEQ) +#define TFLAG_LEN1_SEQ (1 << TBIT_LEN1_SEQ) +#define TFLAG_LEN0123_SEQ_MIRRORS (1 << TBIT_LEN0123_SEQ_MIRRORS) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/getflag.c b/loader/tools/exomizer-3.1/src/getflag.c new file mode 100644 index 0000000..43e3600 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/getflag.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include + +int flagind = 1; +int flagflag = '?'; +const char *flagarg = NULL; + +static void reverse(char **buf, int pos1, int pos2) +{ + char **buf1; + char **buf2; + char *tmp; + + buf1 = buf + pos1; + buf2 = buf + pos2 - 1; + + while (buf1 < buf2) + { + tmp = *buf1; + *buf1 = *buf2; + *buf2 = tmp; + + ++buf1; + --buf2; + } +} + + +int getflag(int argc, char **argv, const char *flags) +{ + int argstart, flagstart, c; + const char *flagp; + + c = -1; + flagarg = NULL; + argstart = flagind; + flagstart = argc; + + /* skip over non-flags */ + while (flagind < argc && argv[flagind][0] != '-') + { + ++flagind; + } + if (flagind == argc) + { + /* no more args */ + flagind = argstart; + return c; + } + /* we have an arg to work with */ + do + { + flagstart = flagind; + if (argv[flagind][1] == '-' && argv[flagind][2] == '\0') + { + /* stop parsing at '--' flag */ + break; + } + c = flagflag = argv[flagind][1]; + if (c == ':' || c == '\0') + { + /* this is an illegal flag */ + c = '?'; + break; + } + /* flag with arg */ + if (argv[flagind][2] != '\0') + { + /* flag-arg in same argv[] */ + flagarg = argv[flagind] + 2; + } + flagp = strchr(flags, c); + if (flagp == NULL) + { + /* this is an unknown flag */ + c = '?'; + break; + } + + if (flagp[1] != ':') + { + /* We expect no argument */ + if (flagarg != NULL) + { + /* error, a simple flag with an argument */ + c = '?'; + } + break; + } + + /* We expect an argument */ + if (flagarg != NULL) + { + /* ... and have one */ + break; + } + + /* flag-arg is in the next argv[] */ + if (flagind + 1 == argc) + { + /* auahh, no flag-arg */ + flagstart = argstart; + c = ':'; + break; + } + + flagarg = argv[++flagind]; + } while (0); + /* skip to next arg */ + ++flagind; + + if (flagstart < flagind && argstart < flagstart) + { + /* we have found some args + * we have also skipped over some non-args + * shuffle the non-flag arg to the end of argv */ + reverse(argv, argstart, flagstart); + reverse(argv, flagstart, flagind); + reverse(argv, argstart, flagind); + } + flagind = argstart + flagind - flagstart; + + return c; +} diff --git a/loader/tools/exomizer-3.1/src/getflag.h b/loader/tools/exomizer-3.1/src/getflag.h new file mode 100644 index 0000000..38ddba8 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/getflag.h @@ -0,0 +1,58 @@ +#ifndef ALREADY_INCLUDED_GETFLAG +#define ALREADY_INCLUDED_GETFLAG +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +/** + * Global state of the getflag function. Set this to 1 to start + * the flag parsing from the beginning. (IN/OUT) + */ +extern int flagind; +/** + * The current flag. (OUT) + */ +extern int flagflag; +/** + * The argument of the current flag, NULL for flags without args. (OUT) + */ +extern const char *flagarg; + +/** + * Incrementally get flags from the given arguments. + * Returns -1 if all flags have been returned. + * Returns '?' if the flag is unknown. Check flagflag for the actual flag. + */ +int getflag(int argc, char *argv[], const char *flags); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/int.h b/loader/tools/exomizer-3.1/src/int.h new file mode 100644 index 0000000..eda1f71 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/int.h @@ -0,0 +1,45 @@ +#ifndef INCLUDED_INT +#define INCLUDED_INT +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +typedef signed char i8; +typedef signed short int i16; +typedef signed int i32; + +typedef unsigned char u8; +typedef unsigned short int u16; +typedef unsigned int u32; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/lex.yy.c b/loader/tools/exomizer-3.1/src/lex.yy.c new file mode 100644 index 0000000..070ada1 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/lex.yy.c @@ -0,0 +1,2768 @@ + +#line 2 "lex.yy.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 118 +#define YY_END_OF_BUFFER 119 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[263] = + { 0, + 0, 0, 0, 0, 0, 0, 114, 114, 0, 0, + 0, 0, 119, 100, 99, 98, 100, 79, 17, 86, + 100, 91, 100, 82, 83, 89, 87, 84, 88, 100, + 90, 73, 85, 18, 75, 92, 76, 100, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 94, 95, 100, 106, 105, 106, 106, + 113, 112, 113, 113, 114, 115, 117, 116, 117, 109, + 108, 98, 78, 74, 80, 0, 0, 0, 0, 0, + 0, 0, 0, 73, 77, 93, 97, 96, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 81, 105, 0, 0, 112, 0, 0, 114, 116, + 109, 108, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 28, 25, 69, 56, 57, + 54, 72, 59, 55, 58, 60, 61, 46, 50, 40, + 49, 30, 31, 32, 67, 63, 65, 27, 66, 62, + 64, 53, 52, 19, 20, 21, 68, 41, 26, 35, + 37, 36, 38, 71, 70, 48, 29, 47, 51, 39, + 22, 23, 24, 45, 43, 33, 44, 34, 42, 0, + + 0, 101, 0, 110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 14, 0, 0, 0, 0, + 0, 0, 16, 0, 10, 2, 3, 0, 0, 0, + 0, 0, 0, 15, 102, 103, 0, 0, 0, 0, + 4, 9, 0, 0, 0, 0, 6, 104, 111, 0, + 0, 11, 12, 0, 0, 0, 7, 5, 13, 0, + 107, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 5, 6, 7, 8, 9, 10, 1, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 20, 21, 22, + 23, 24, 25, 1, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 36, + 1, 1, 1, 1, 51, 1, 52, 53, 54, 55, + + 56, 57, 58, 59, 60, 61, 36, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 36, 1, 76, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[77] = + { 0, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 4, 5, + 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 1 + } ; + +static const flex_int16_t yy_base[272] = + { 0, + 0, 0, 74, 76, 78, 80, 593, 592, 82, 84, + 580, 579, 595, 600, 600, 600, 591, 570, 600, 600, + 0, 600, 582, 600, 600, 600, 600, 600, 600, 71, + 600, 572, 600, 600, 600, 567, 600, 566, 74, 125, + 84, 72, 70, 568, 69, 79, 129, 76, 131, 134, + 92, 150, 166, 567, 566, 509, 600, 600, 581, 122, + 600, 600, 580, 153, 0, 600, 600, 600, 579, 0, + 177, 600, 600, 0, 600, 65, 145, 180, 173, 150, + 122, 170, 171, 562, 600, 600, 560, 600, 201, 202, + 208, 218, 185, 194, 229, 223, 230, 231, 248, 240, + + 241, 260, 251, 273, 245, 253, 279, 264, 277, 278, + 316, 318, 313, 293, 204, 332, 345, 329, 306, 320, + 325, 600, 600, 337, 310, 600, 319, 354, 0, 600, + 0, 562, 324, 328, 359, 358, 362, 371, 350, 600, + 374, 379, 376, 361, 367, 558, 557, 556, 555, 554, + 553, 552, 551, 550, 549, 548, 547, 546, 545, 544, + 541, 535, 505, 504, 499, 498, 496, 495, 494, 493, + 491, 489, 488, 486, 484, 477, 389, 349, 319, 305, + 304, 297, 292, 263, 258, 254, 249, 246, 236, 234, + 227, 205, 149, 148, 141, 140, 110, 100, 87, 379, + + 383, 600, 385, 600, 386, 391, 390, 387, 394, 399, + 398, 396, 416, 394, 600, 600, 413, 413, 415, 412, + 415, 416, 600, 417, 600, 600, 600, 426, 416, 426, + 436, 423, 425, 600, 600, 600, 436, 442, 448, 446, + 600, 600, 441, 446, 448, 444, 600, 600, 600, 463, + 465, 600, 600, 465, 467, 455, 600, 600, 600, 461, + 600, 600, 526, 531, 536, 541, 546, 102, 548, 553, + 558 + } ; + +static const flex_int16_t yy_def[272] = + { 0, + 262, 1, 263, 263, 264, 264, 265, 265, 266, 266, + 267, 267, 262, 262, 262, 262, 262, 262, 262, 262, + 268, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 270, 262, 262, 262, 262, 271, + 262, 262, 262, 268, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 269, 262, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 262, 262, 262, 262, 262, 262, 262, 270, 262, + 271, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 262, + + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 0, 262, 262, 262, 262, 262, 262, 262, 262, + 262 + } ; + +static const flex_int16_t yy_nxt[677] = + { 0, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 44, 44, 45, 46, 44, 47, 44, 48, 49, + 50, 44, 51, 52, 53, 44, 44, 44, 54, 55, + 14, 39, 40, 41, 42, 43, 44, 44, 44, 45, + 46, 47, 44, 48, 49, 50, 44, 51, 52, 53, + 44, 44, 44, 54, 55, 56, 58, 59, 58, 59, + 62, 63, 62, 63, 68, 69, 68, 69, 88, 88, + 60, 88, 60, 88, 64, 88, 64, 76, 88, 77, + + 78, 102, 89, 88, 79, 74, 88, 104, 80, 103, + 81, 88, 90, 82, 134, 109, 105, 91, 83, 88, + 99, 100, 106, 76, 101, 77, 78, 102, 89, 88, + 79, 113, 104, 80, 103, 81, 114, 90, 82, 134, + 109, 105, 91, 83, 88, 99, 100, 106, 88, 101, + 88, 124, 92, 88, 93, 125, 113, 107, 94, 88, + 88, 114, 95, 96, 143, 97, 111, 88, 88, 88, + 112, 98, 108, 110, 135, 142, 115, 124, 92, 116, + 93, 125, 127, 107, 94, 88, 128, 95, 96, 143, + 97, 118, 111, 132, 117, 112, 98, 108, 110, 144, + + 135, 142, 115, 140, 88, 116, 133, 136, 127, 119, + 145, 141, 128, 88, 120, 121, 137, 118, 138, 117, + 88, 88, 139, 88, 88, 144, 151, 88, 146, 140, + 147, 187, 133, 136, 119, 145, 141, 88, 152, 120, + 121, 137, 88, 138, 148, 149, 88, 139, 88, 88, + 88, 151, 154, 88, 146, 88, 147, 187, 156, 88, + 88, 150, 153, 152, 88, 88, 155, 88, 88, 148, + 88, 149, 88, 88, 157, 158, 159, 88, 154, 88, + 162, 160, 88, 88, 156, 172, 150, 165, 153, 163, + 164, 155, 88, 168, 161, 173, 88, 88, 88, 157, + + 169, 158, 159, 179, 174, 162, 177, 160, 166, 167, + 172, 88, 88, 165, 163, 164, 88, 178, 168, 161, + 173, 170, 171, 88, 88, 88, 169, 175, 176, 179, + 174, 177, 88, 166, 167, 88, 186, 88, 88, 88, + 202, 180, 178, 182, 88, 197, 170, 171, 88, 184, + 199, 88, 175, 176, 196, 185, 181, 203, 183, 188, + 189, 186, 205, 198, 88, 190, 202, 180, 88, 182, + 191, 197, 206, 200, 184, 201, 199, 194, 195, 196, + 185, 181, 203, 183, 204, 188, 189, 205, 198, 207, + 208, 190, 212, 192, 193, 209, 191, 206, 200, 211, + + 201, 213, 194, 195, 216, 210, 214, 215, 88, 217, + 204, 220, 218, 221, 222, 207, 208, 212, 192, 193, + 223, 209, 219, 224, 226, 211, 225, 213, 227, 216, + 210, 228, 214, 215, 217, 229, 233, 220, 218, 221, + 222, 234, 230, 235, 236, 237, 223, 219, 238, 224, + 226, 225, 231, 239, 227, 240, 241, 228, 242, 243, + 229, 233, 246, 232, 247, 244, 248, 234, 230, 235, + 236, 237, 249, 250, 238, 251, 254, 231, 239, 252, + 240, 245, 241, 242, 253, 243, 255, 246, 232, 247, + 256, 244, 248, 257, 258, 259, 88, 260, 249, 250, + + 261, 251, 254, 88, 252, 88, 245, 88, 88, 253, + 88, 255, 88, 88, 88, 88, 256, 88, 88, 257, + 258, 259, 260, 88, 88, 261, 57, 57, 57, 57, + 57, 61, 61, 61, 61, 61, 65, 65, 65, 65, + 65, 67, 67, 67, 67, 67, 70, 70, 70, 70, + 70, 87, 87, 129, 88, 129, 129, 129, 131, 131, + 88, 131, 131, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 132, 88, + 84, 130, 126, 123, 122, 88, 88, 88, 86, 85, + 84, 75, 73, 72, 262, 71, 71, 66, 66, 13, + + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262 + } ; + +static const flex_int16_t yy_chk[677] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, + 5, 5, 6, 6, 9, 9, 10, 10, 45, 43, + 3, 42, 4, 39, 5, 48, 6, 30, 46, 30, + + 30, 42, 39, 41, 30, 268, 199, 45, 30, 43, + 30, 51, 39, 30, 76, 48, 46, 39, 30, 198, + 41, 41, 46, 30, 41, 30, 30, 42, 39, 197, + 30, 51, 45, 30, 43, 30, 51, 39, 30, 76, + 48, 46, 39, 30, 40, 41, 41, 46, 47, 41, + 49, 60, 40, 50, 40, 60, 51, 47, 40, 196, + 195, 51, 40, 40, 81, 40, 50, 194, 193, 52, + 50, 40, 47, 49, 77, 80, 52, 60, 40, 52, + 40, 60, 64, 47, 40, 53, 64, 40, 40, 81, + 40, 53, 50, 71, 52, 50, 40, 47, 49, 82, + + 77, 80, 52, 79, 93, 52, 71, 78, 64, 53, + 83, 79, 64, 94, 53, 53, 78, 53, 78, 52, + 89, 90, 78, 115, 192, 82, 93, 91, 89, 79, + 90, 115, 71, 78, 53, 83, 79, 92, 94, 53, + 53, 78, 96, 78, 91, 92, 191, 78, 95, 97, + 98, 93, 96, 190, 89, 189, 90, 115, 98, 100, + 101, 92, 95, 94, 105, 188, 97, 99, 187, 91, + 103, 92, 106, 186, 98, 99, 99, 185, 96, 102, + 100, 99, 184, 108, 98, 105, 92, 102, 95, 101, + 101, 97, 104, 103, 99, 106, 109, 110, 107, 98, + + 104, 99, 99, 110, 107, 100, 108, 99, 102, 102, + 105, 183, 114, 102, 101, 101, 182, 109, 103, 99, + 106, 104, 104, 181, 180, 119, 104, 107, 107, 110, + 107, 108, 113, 102, 102, 111, 114, 112, 179, 120, + 125, 111, 109, 112, 121, 120, 104, 104, 118, 113, + 121, 116, 107, 107, 119, 113, 111, 127, 112, 116, + 116, 114, 133, 120, 117, 116, 125, 111, 178, 112, + 117, 120, 134, 124, 113, 124, 121, 118, 118, 119, + 113, 111, 127, 112, 128, 116, 116, 133, 120, 135, + 136, 116, 139, 117, 117, 137, 117, 134, 124, 138, + + 124, 141, 118, 118, 144, 137, 142, 143, 177, 145, + 128, 201, 200, 203, 205, 135, 136, 139, 117, 117, + 206, 137, 200, 207, 209, 138, 208, 141, 210, 144, + 137, 211, 142, 143, 145, 212, 214, 201, 200, 203, + 205, 217, 213, 218, 219, 220, 206, 200, 221, 207, + 209, 208, 213, 222, 210, 224, 228, 211, 229, 230, + 212, 214, 232, 213, 233, 231, 237, 217, 213, 218, + 219, 220, 238, 239, 221, 240, 245, 213, 222, 243, + 224, 231, 228, 229, 244, 230, 246, 232, 213, 233, + 250, 231, 237, 251, 254, 255, 176, 256, 238, 239, + + 260, 240, 245, 175, 243, 174, 231, 173, 172, 244, + 171, 246, 170, 169, 168, 167, 250, 166, 165, 251, + 254, 255, 256, 164, 163, 260, 263, 263, 263, 263, + 263, 264, 264, 264, 264, 264, 265, 265, 265, 265, + 265, 266, 266, 266, 266, 266, 267, 267, 267, 267, + 267, 269, 269, 270, 162, 270, 270, 270, 271, 271, + 161, 271, 271, 160, 159, 158, 157, 156, 155, 154, + 153, 152, 151, 150, 149, 148, 147, 146, 132, 87, + 84, 69, 63, 59, 56, 55, 54, 44, 38, 36, + 32, 23, 18, 17, 13, 12, 11, 8, 7, 262, + + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "asm.yy" +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ +/* scanner for a simple assembler */ +#line 30 "asm.yy" +#include +#include +#include "int.h" +#include "buf.h" +#include "parse.h" +#include "asm.tab.h" + +#define MAX_SRC_BUFFER_DEPTH 10 +static YY_BUFFER_STATE src_buffers[MAX_SRC_BUFFER_DEPTH]; +static int src_buffer_depth = 0; + +static char *strdupped_get(char *text); +static int strdupped_cmp(const void *a, const void *b); +static void strdupped_free(void *a); + +int num_lines = 1; +int push_state_skip = 0; +int push_state_init = 0; +int push_state_macro = 0; +struct vec strdupped[1]; + +#line 731 "lex.yy.c" + +#line 733 "lex.yy.c" + +#define INITIAL 0 +#define SKIP 1 +#define SKIP_ALL 2 +#define QUOTED_STRING 3 +#define SKIP_LINE 4 +#define MACROO 5 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr ); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + + static int yy_start_stack_ptr = 0; + static int yy_start_stack_depth = 0; + static int *yy_start_stack = NULL; + + static void yy_push_state ( int _new_state ); + + static void yy_pop_state ( void ); + + static int yy_top_state ( void ); + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 59 "asm.yy" + + +#line 62 "asm.yy" + if(push_state_init) + {push_state_init = 0; yy_push_state(INITIAL); } + if(push_state_skip) + {push_state_skip = 0; yy_push_state(SKIP); } + if(push_state_macro) + {push_state_macro = 0; yy_push_state(MACROO); } + +#line 976 "lex.yy.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 263 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_current_state != 262 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 69 "asm.yy" +return IF; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 70 "asm.yy" +BEGIN(SKIP_ALL); + YY_BREAK +case 3: +YY_RULE_SETUP +#line 71 "asm.yy" +BEGIN(SKIP); + YY_BREAK +case 4: +YY_RULE_SETUP +#line 72 "asm.yy" +yy_pop_state(); + YY_BREAK +case 5: +YY_RULE_SETUP +#line 74 "asm.yy" +return INCLUDE; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 75 "asm.yy" +return MACRO; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 77 "asm.yy" +return DEFINED; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 78 "asm.yy" +return ORG; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 79 "asm.yy" +return ERROR; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 80 "asm.yy" +return ECHO1; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 81 "asm.yy" +return INCBIN; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 82 "asm.yy" +return INCLEN; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 83 "asm.yy" +return INCWORD; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 84 "asm.yy" +return RES; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 85 "asm.yy" +return WORD; + YY_BREAK +case 16: +YY_RULE_SETUP +#line 86 "asm.yy" +return BYTE; + YY_BREAK +case 17: +YY_RULE_SETUP +#line 88 "asm.yy" +BEGIN(QUOTED_STRING); + YY_BREAK +case 18: +YY_RULE_SETUP +#line 89 "asm.yy" +BEGIN(SKIP_LINE); + YY_BREAK +case 19: +YY_RULE_SETUP +#line 91 "asm.yy" +return LDA; + YY_BREAK +case 20: +YY_RULE_SETUP +#line 92 "asm.yy" +return LDX; + YY_BREAK +case 21: +YY_RULE_SETUP +#line 93 "asm.yy" +return LDY; + YY_BREAK +case 22: +YY_RULE_SETUP +#line 94 "asm.yy" +return STA; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 95 "asm.yy" +return STX; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 96 "asm.yy" +return STY; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 97 "asm.yy" +return AND; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 98 "asm.yy" +return ORA; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 99 "asm.yy" +return EOR; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 100 "asm.yy" +return ADC; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 101 "asm.yy" +return SBC; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 102 "asm.yy" +return CMP; + YY_BREAK +case 31: +YY_RULE_SETUP +#line 103 "asm.yy" +return CPX; + YY_BREAK +case 32: +YY_RULE_SETUP +#line 104 "asm.yy" +return CPY; + YY_BREAK +case 33: +YY_RULE_SETUP +#line 106 "asm.yy" +return TSX; + YY_BREAK +case 34: +YY_RULE_SETUP +#line 107 "asm.yy" +return TXS; + YY_BREAK +case 35: +YY_RULE_SETUP +#line 108 "asm.yy" +return PHA; + YY_BREAK +case 36: +YY_RULE_SETUP +#line 109 "asm.yy" +return PLA; + YY_BREAK +case 37: +YY_RULE_SETUP +#line 110 "asm.yy" +return PHP; + YY_BREAK +case 38: +YY_RULE_SETUP +#line 111 "asm.yy" +return PLP; + YY_BREAK +case 39: +YY_RULE_SETUP +#line 112 "asm.yy" +return SEI; + YY_BREAK +case 40: +YY_RULE_SETUP +#line 113 "asm.yy" +return CLI; + YY_BREAK +case 41: +YY_RULE_SETUP +#line 114 "asm.yy" +return NOP; + YY_BREAK +case 42: +YY_RULE_SETUP +#line 115 "asm.yy" +return TYA; + YY_BREAK +case 43: +YY_RULE_SETUP +#line 116 "asm.yy" +return TAY; + YY_BREAK +case 44: +YY_RULE_SETUP +#line 117 "asm.yy" +return TXA; + YY_BREAK +case 45: +YY_RULE_SETUP +#line 118 "asm.yy" +return TAX; + YY_BREAK +case 46: +YY_RULE_SETUP +#line 119 "asm.yy" +return CLC; + YY_BREAK +case 47: +YY_RULE_SETUP +#line 120 "asm.yy" +return SEC; + YY_BREAK +case 48: +YY_RULE_SETUP +#line 121 "asm.yy" +return RTS; + YY_BREAK +case 49: +YY_RULE_SETUP +#line 122 "asm.yy" +return CLV; + YY_BREAK +case 50: +YY_RULE_SETUP +#line 123 "asm.yy" +return CLD; + YY_BREAK +case 51: +YY_RULE_SETUP +#line 124 "asm.yy" +return SED; + YY_BREAK +case 52: +YY_RULE_SETUP +#line 126 "asm.yy" +return JSR; + YY_BREAK +case 53: +YY_RULE_SETUP +#line 127 "asm.yy" +return JMP; + YY_BREAK +case 54: +YY_RULE_SETUP +#line 128 "asm.yy" +return BEQ; + YY_BREAK +case 55: +YY_RULE_SETUP +#line 129 "asm.yy" +return BNE; + YY_BREAK +case 56: +YY_RULE_SETUP +#line 130 "asm.yy" +return BCC; + YY_BREAK +case 57: +YY_RULE_SETUP +#line 131 "asm.yy" +return BCS; + YY_BREAK +case 58: +YY_RULE_SETUP +#line 132 "asm.yy" +return BPL; + YY_BREAK +case 59: +YY_RULE_SETUP +#line 133 "asm.yy" +return BMI; + YY_BREAK +case 60: +YY_RULE_SETUP +#line 134 "asm.yy" +return BVC; + YY_BREAK +case 61: +YY_RULE_SETUP +#line 135 "asm.yy" +return BVS; + YY_BREAK +case 62: +YY_RULE_SETUP +#line 136 "asm.yy" +return INX; + YY_BREAK +case 63: +YY_RULE_SETUP +#line 137 "asm.yy" +return DEX; + YY_BREAK +case 64: +YY_RULE_SETUP +#line 138 "asm.yy" +return INY; + YY_BREAK +case 65: +YY_RULE_SETUP +#line 139 "asm.yy" +return DEY; + YY_BREAK +case 66: +YY_RULE_SETUP +#line 140 "asm.yy" +return INC; + YY_BREAK +case 67: +YY_RULE_SETUP +#line 141 "asm.yy" +return DEC; + YY_BREAK +case 68: +YY_RULE_SETUP +#line 142 "asm.yy" +return LSR; + YY_BREAK +case 69: +YY_RULE_SETUP +#line 143 "asm.yy" +return ASL; + YY_BREAK +case 70: +YY_RULE_SETUP +#line 144 "asm.yy" +return ROR; + YY_BREAK +case 71: +YY_RULE_SETUP +#line 145 "asm.yy" +return ROL; + YY_BREAK +case 72: +YY_RULE_SETUP +#line 146 "asm.yy" +return BIT; + YY_BREAK +case 73: +YY_RULE_SETUP +#line 148 "asm.yy" +{ yylval.num = atoi(yytext); return NUMBER; } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 150 "asm.yy" +{ yylval.num = strtol(yytext + 1, NULL, 16); return NUMBER; } + YY_BREAK +case 75: +YY_RULE_SETUP +#line 152 "asm.yy" +return LT; + YY_BREAK +case 76: +YY_RULE_SETUP +#line 153 "asm.yy" +return GT; + YY_BREAK +case 77: +YY_RULE_SETUP +#line 154 "asm.yy" +return EQ; + YY_BREAK +case 78: +YY_RULE_SETUP +#line 155 "asm.yy" +return NEQ; + YY_BREAK +case 79: +YY_RULE_SETUP +#line 156 "asm.yy" +return LNOT; + YY_BREAK +case 80: +YY_RULE_SETUP +#line 157 "asm.yy" +return LAND; + YY_BREAK +case 81: +YY_RULE_SETUP +#line 158 "asm.yy" +return LOR; + YY_BREAK +case 82: +YY_RULE_SETUP +#line 160 "asm.yy" +return LPAREN; + YY_BREAK +case 83: +YY_RULE_SETUP +#line 161 "asm.yy" +return RPAREN; + YY_BREAK +case 84: +YY_RULE_SETUP +#line 162 "asm.yy" +return COMMA; + YY_BREAK +case 85: +YY_RULE_SETUP +#line 163 "asm.yy" +return COLON; + YY_BREAK +case 86: +YY_RULE_SETUP +#line 164 "asm.yy" +return HASH; + YY_BREAK +case 87: +YY_RULE_SETUP +#line 165 "asm.yy" +return PLUS; + YY_BREAK +case 88: +YY_RULE_SETUP +#line 166 "asm.yy" +return MINUS; + YY_BREAK +case 89: +YY_RULE_SETUP +#line 167 "asm.yy" +return MULT; + YY_BREAK +case 90: +YY_RULE_SETUP +#line 168 "asm.yy" +return DIV; + YY_BREAK +case 91: +YY_RULE_SETUP +#line 169 "asm.yy" +return MOD; + YY_BREAK +case 92: +YY_RULE_SETUP +#line 171 "asm.yy" +return ASSIGN; + YY_BREAK +case 93: +YY_RULE_SETUP +#line 172 "asm.yy" +return GUESS; + YY_BREAK +case 94: +YY_RULE_SETUP +#line 174 "asm.yy" +return X; + YY_BREAK +case 95: +YY_RULE_SETUP +#line 175 "asm.yy" +return Y; + YY_BREAK +case 96: +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 177 "asm.yy" +{ yylval.str = strdupped_get(yytext); return LABEL; } + YY_BREAK +case 97: +YY_RULE_SETUP +#line 178 "asm.yy" +{ yylval.str = strdupped_get(yytext); return SYMBOL; } + YY_BREAK +case 98: +/* rule 98 can match eol */ +YY_RULE_SETUP +#line 180 "asm.yy" +++num_lines; + YY_BREAK +case 99: +YY_RULE_SETUP +#line 182 "asm.yy" +/* eat whitespace */ + YY_BREAK +case 100: +YY_RULE_SETUP +#line 184 "asm.yy" +printf("unknown character found %s\n", yytext); + YY_BREAK +case 101: +YY_RULE_SETUP +#line 186 "asm.yy" +yy_push_state(SKIP_ALL); + YY_BREAK +case 102: +YY_RULE_SETUP +#line 187 "asm.yy" +{ yy_pop_state(); return IF; } + YY_BREAK +case 103: +YY_RULE_SETUP +#line 188 "asm.yy" +BEGIN(INITIAL); + YY_BREAK +case 104: +YY_RULE_SETUP +#line 189 "asm.yy" +yy_pop_state(); + YY_BREAK +case 105: +/* rule 105 can match eol */ +YY_RULE_SETUP +#line 190 "asm.yy" +++num_lines; + YY_BREAK +case 106: +YY_RULE_SETUP +#line 191 "asm.yy" + + YY_BREAK +case 107: +YY_RULE_SETUP +#line 193 "asm.yy" +yy_pop_state(); + YY_BREAK +case 108: +YY_RULE_SETUP +#line 194 "asm.yy" +{ yylval.str = yytext; return MACRO_STRING; } + YY_BREAK +case 109: +/* rule 109 can match eol */ +YY_RULE_SETUP +#line 195 "asm.yy" +{ yylval.str = yytext; return MACRO_STRING; } + YY_BREAK +case 110: +YY_RULE_SETUP +#line 197 "asm.yy" +yy_push_state(SKIP_ALL); + YY_BREAK +case 111: +YY_RULE_SETUP +#line 198 "asm.yy" +yy_pop_state(); + YY_BREAK +case 112: +/* rule 112 can match eol */ +YY_RULE_SETUP +#line 199 "asm.yy" +++num_lines; + YY_BREAK +case 113: +YY_RULE_SETUP +#line 200 "asm.yy" + + YY_BREAK +case 114: +/* rule 114 can match eol */ +YY_RULE_SETUP +#line 202 "asm.yy" +{ + /* multi-line string with correct line count */ + char *p = strdupped_get(yytext); + yylval.str = p; + while (*p != '\0') + { + if (*p++ == '\n') + { + ++num_lines; + } + } + return STRING; +} + YY_BREAK +case 115: +YY_RULE_SETUP +#line 215 "asm.yy" +BEGIN(INITIAL); + YY_BREAK +case 116: +/* rule 116 can match eol */ +YY_RULE_SETUP +#line 217 "asm.yy" +{ ++num_lines; BEGIN(INITIAL); } + YY_BREAK +case 117: +YY_RULE_SETUP +#line 218 "asm.yy" + + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(SKIP): +case YY_STATE_EOF(SKIP_ALL): +case YY_STATE_EOF(QUOTED_STRING): +case YY_STATE_EOF(SKIP_LINE): +case YY_STATE_EOF(MACROO): +#line 220 "asm.yy" +{ + if(--src_buffer_depth == 0) + { + yyterminate(); + } + else + { + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(src_buffers[src_buffer_depth]); + } + } + YY_BREAK +case 118: +YY_RULE_SETUP +#line 232 "asm.yy" +ECHO; + YY_BREAK +#line 1659 "lex.yy.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 263 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 263 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 262); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp ) +{ + char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + + static void yy_push_state (int _new_state ) +{ + if ( (yy_start_stack_ptr) >= (yy_start_stack_depth) ) + { + yy_size_t new_size; + + (yy_start_stack_depth) += YY_START_STACK_INCR; + new_size = (yy_size_t) (yy_start_stack_depth) * sizeof( int ); + + if ( ! (yy_start_stack) ) + (yy_start_stack) = (int *) yyalloc( new_size ); + + else + (yy_start_stack) = (int *) yyrealloc( + (void *) (yy_start_stack), new_size ); + + if ( ! (yy_start_stack) ) + YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); + } + + (yy_start_stack)[(yy_start_stack_ptr)++] = YY_START; + + BEGIN(_new_state); +} + + static void yy_pop_state (void) +{ + if ( --(yy_start_stack_ptr) < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN((yy_start_stack)[(yy_start_stack_ptr)]); +} + + static int yy_top_state (void) +{ + return (yy_start_stack)[(yy_start_stack_ptr) - 1]; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + + (yy_start_stack_ptr) = 0; + (yy_start_stack_depth) = 0; + (yy_start_stack) = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Destroy the start condition stack. */ + yyfree( (yy_start_stack) ); + (yy_start_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 232 "asm.yy" + + +void scanner_init(void) +{ + vec_init(strdupped, sizeof(char*)); +} + +void asm_src_buffer_push(struct buf *buffer) +{ + if(src_buffer_depth == MAX_SRC_BUFFER_DEPTH) + { + fprintf(stderr, "source buffers nested too deep\n"); + exit(1); + } + src_buffers[src_buffer_depth++] = YY_CURRENT_BUFFER; + yy_scan_bytes(buf_data(buffer), buf_size(buffer)); +} + +static char *strdupped_get(char *text) +{ + char **pp; + /*printf("get \"%s\" => ", text);*/ + if(vec_insert_uniq(strdupped, strdupped_cmp, &text, (void*)&pp)) + { + /* replace the pointer to since will be reused */ + *pp = strdup(text); + } + /*printf("%p\n", *pp);*/ + return *pp; +} + +static void strdupped_free(void *a) +{ + char *b = *(char**)a; + /*printf("free => %p \"%s\"\n", b, b);*/ + free(b); +} + +static int strdupped_cmp(const void *a, const void *b) +{ + char *c = *(char**)a; + char *d = *(char**)b; + + return strcmp(c, d); +} + +void scanner_free(void) +{ + vec_free(strdupped, strdupped_free); +} + + +void silence_warnings_about_unused_functions(void) +{ + yyunput(0, NULL); + input(); + yy_top_state(); +} + diff --git a/loader/tools/exomizer-3.1/src/log.c b/loader/tools/exomizer-3.1/src/log.c new file mode 100644 index 0000000..5f0a1b4 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/log.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This file is a part of the Exomizer v1.1 release + * + */ + +#include +#include +#include +#include +#include "log.h" + +#ifdef WIN32 +#define vsnprintf _vsnprintf +#endif + +struct log_output { + enum log_level min; + enum log_level max; + FILE *stream; + int isatty; + log_formatter_f *f; +}; + +struct log_ctx { + enum log_level level; + int out_len; + struct log_output *out; + int buf_len; + char *buf; +}; + +struct log_ctx *G_log_ctx = NULL; +enum log_level G_log_level = LOG_MIN; +enum log_level G_log_log_level = 0; +int G_log_tty_only = 0; + +struct log_ctx *log_new(void) +{ + struct log_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + { + fprintf(stderr, + "fatal error, can't allocate memory for log context\n"); + exit(1); + } + ctx->level = LOG_NORMAL; + ctx->out_len = 0; + ctx->out = NULL; + ctx->buf_len = 0; + ctx->buf = NULL; + + return ctx; +} + +/* log_delete closes all added output streams + * and files except for stdout and stderr + */ +void log_delete(struct log_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->out_len; ++i) + { + FILE *file = ctx->out[i].stream; + if (file != stderr && file != stdout) + { + fclose(file); + } + } + free(ctx->out); + free(ctx->buf); + free(ctx); +} + +void log_set_level(struct log_ctx *ctx, /* IN/OUT */ + enum log_level level) /* IN */ +{ + ctx->level = level; +} + +void log_add_output_stream(struct log_ctx *ctx, /* IN/OUT */ + enum log_level min, /* IN */ + enum log_level max, /* IN */ + log_formatter_f * default_f, /* IN */ + FILE * out_stream) /* IN */ +{ + struct log_output *out; + + ctx->out_len += 1; + ctx->out = realloc(ctx->out, ctx->out_len * sizeof(*(ctx->out))); + if (ctx->out == NULL) + { + fprintf(stderr, + "fatal error, can't allocate memory for log output\n"); + exit(1); + } + out = &(ctx->out[ctx->out_len - 1]); + out->min = min; + out->max = max; + out->stream = out_stream; + out->isatty = isatty(fileno(out_stream)); + out->f = default_f; +} + +void raw_log_formatter(FILE * out, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + const char *log) /* IN */ +{ + fprintf(out, "%s", log); + fflush(out); +} + +void log_vlog(struct log_ctx *ctx, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + log_formatter_f * f, /* IN */ + const char *printf_str, /* IN */ + va_list argp) +{ + int len; + int i; + + if (ctx->level < level) + { + /* don't log this */ + return; + } + + len = 0; + do + { + if (len >= ctx->buf_len) + { + ctx->buf_len = len + 1024; + ctx->buf = realloc(ctx->buf, ctx->buf_len); + if (ctx->buf == NULL) + { + fprintf(stderr, + "fatal error, can't allocate memory for log log\n"); + exit(1); + } + } + len = vsnprintf(ctx->buf, ctx->buf_len, printf_str, argp); + } + + while (len >= ctx->buf_len); + + for (i = 0; i < ctx->out_len; ++i) + { + struct log_output *o = &ctx->out[i]; + log_formatter_f *of = f; + + if (level >= o->min && level <= o->max && + (G_log_tty_only == 0 || o->isatty != 0)) + { + /* generate log for this output */ + if (of == NULL) + { + of = o->f; + } + if (of != NULL) + { + of(o->stream, level, context, ctx->buf); + } else + { + fprintf(o->stream, "%s\n", ctx->buf); + fflush(o->stream); + } + } + } +} + +void log_log_default(const char *printf_str, /* IN */ + ...) +{ + va_list argp; + va_start(argp, printf_str); + log_vlog(G_log_ctx, G_log_log_level, + NULL, raw_log_formatter, printf_str, argp); +} + +void log_log(struct log_ctx *ctx, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + log_formatter_f * f, /* IN */ + const char *printf_str, /* IN */ + ...) +{ + va_list argp; + va_start(argp, printf_str); + log_vlog(ctx, level, context, f, printf_str, argp); +} + +void hex_dump(int level, unsigned char *p, int len) +{ + int i; + int j; + for(i = 0; i < len;) + { + LOG(level, ("%02x", p[i])); + ++i; + if(i == len || (i & 15) == 0) + { + LOG(level, ("\t\"")); + for(j = (i - 1) & ~15; j < i; ++j) + { + unsigned char c = p[j]; + if(!isprint(c)) + { + c = '.'; + } + LOG(level, ("%c", c)); + } + LOG(level, ("\"\n")); + } + else + { + LOG(level, (",")); + } + } +} diff --git a/loader/tools/exomizer-3.1/src/log.h b/loader/tools/exomizer-3.1/src/log.h new file mode 100644 index 0000000..28663dd --- /dev/null +++ b/loader/tools/exomizer-3.1/src/log.h @@ -0,0 +1,158 @@ +#ifndef ALREADY_INCLUDED_LOG +#define ALREADY_INCLUDED_LOG +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include + +#ifndef __GNUC__ +#define __attribute__(x) /*NOTHING*/ +#endif + +enum log_level { + LOG_MIN = -99, + LOG_FATAL = -40, + LOG_ERROR = -30, + LOG_WARNING = -20, + LOG_TERSE = -15, + LOG_BRIEF = -10, + LOG_NORMAL = 0, + LOG_VERBOSE = 10, + LOG_TRACE = 20, + LOG_DEBUG = 30, + LOG_DUMP = 40, + LOG_MAX = 99 +}; + +typedef void log_formatter_f(FILE * out, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + const char *); /* IN */ + +/* + * this log output function adds nothing + */ +void raw_log_formatter(FILE * out, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + const char *log); /* IN */ + + +struct log_output; + +struct log_ctx; + +struct log_ctx *log_new(void); + +/* log_delete closes all added output streams + * and files except for stdout and stderr + */ +void log_delete(struct log_ctx *ctx); + +void log_set_level(struct log_ctx *ctx, /* IN/OUT */ + enum log_level level); /* IN */ + +void log_add_output_stream(struct log_ctx *ctx, /* IN/OUT */ + enum log_level min, /* IN */ + enum log_level max, /* IN */ + log_formatter_f * default_f, /* IN */ + FILE * out_stream); /* IN */ + +void log_vlog(struct log_ctx *ctx, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + log_formatter_f * f, /* IN */ + const char *printf_str, /* IN */ + va_list argp); + + +void log_log_default(const char *printf_str, /* IN */ + ...) + __attribute__((format(printf,1,2))); + +/* some helper macros */ + +extern struct log_ctx *G_log_ctx; +extern enum log_level G_log_level; +extern enum log_level G_log_log_level; +extern int G_log_tty_only; + +#define LOG_SET_LEVEL(L) \ +do { \ + log_set_level(G_log_ctx, (L)); \ + G_log_level = (L); \ +} while(0) + +#define LOG_INIT(L) \ +do { \ + G_log_ctx = log_new(); \ + log_set_level(G_log_ctx, (L)); \ + G_log_level = (L); \ +} while(0) + +#define LOG_INIT_CONSOLE(X) \ +do { \ + G_log_ctx = log_new(); \ + log_set_level(G_log_ctx, (X)); \ + G_log_level = (X); \ + log_add_output_stream(G_log_ctx, LOG_WARNING, LOG_MAX, NULL, stdout); \ + log_add_output_stream(G_log_ctx, LOG_MIN, LOG_WARNING - 1, NULL, stderr); \ +} while(0) + +#define LOG_FREE log_delete(G_log_ctx) + +#define IS_LOGGABLE(L) (G_log_level >= (L)) + +#define LOG(L, M) \ +do { \ + if(IS_LOGGABLE(L)) { \ + G_log_log_level = (L); \ + G_log_tty_only = 0; \ + log_log_default M; \ + } \ +} while(0) + +#define LOG_TTY(L, M) \ +do { \ + if(IS_LOGGABLE(L)) { \ + G_log_log_level = (L); \ + G_log_tty_only = 1; \ + log_log_default M; \ + } \ +} while(0) + +void hex_dump(int level, unsigned char *p, int len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/map.c b/loader/tools/exomizer-3.1/src/map.c new file mode 100644 index 0000000..f95024d --- /dev/null +++ b/loader/tools/exomizer-3.1/src/map.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2006 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "map.h" +#include "log.h" +#include + +static int map_entry_cmp(const void *a, const void *b) +{ + struct map_entry *entry_a; + struct map_entry *entry_b; + int val; + + entry_a = (struct map_entry*)a; + entry_b = (struct map_entry*)b; + + val = strcmp(entry_a->key, entry_b->key); + + return val; +} + +void map_init(struct map *m) +{ + vec_init(&m->vec, sizeof(struct map_entry)); +} +void map_clear(struct map *m) +{ + vec_clear(&m->vec, NULL); +} + +void map_free(struct map *m) +{ + vec_free(&m->vec, NULL); +} + +void *map_put(struct map *m, const char *key, void *value) +{ + struct map_entry e; + int pos; + void *prev_value = NULL; + + e.key = key; + e.value = value; + pos = vec_find(&m->vec, map_entry_cmp, &e); + if(pos == -1) + { + /* error, find failed */ + LOG(LOG_ERROR, ("map_put: vec_find() internal error\n")); + exit(1); + } + if(pos >= 0) + { + /* previous value exists, get value then set */ + struct map_entry *prev_e; + prev_e = vec_get(&m->vec, pos); + prev_value = prev_e->value; + vec_set(&m->vec, pos, &e); + } + else + { + /* no value exists, insert */ + vec_insert(&m->vec, -(pos + 2), &e); + } + return prev_value; +} + +static struct map_entry *get(const struct map *m, const char *key) +{ + struct map_entry e; + int pos; + struct map_entry *out = NULL; + + e.key = key; + pos = vec_find(&m->vec, map_entry_cmp, &e); + if(pos == -1) + { + /* error, find failed */ + LOG(LOG_ERROR, ("map_put: vec_find() internal error\n")); + exit(1); + } + if(pos >= 0) + { + out = vec_get(&m->vec, pos); + } + return out; +} + +int map_contains_key(const struct map *m, const char *key) +{ + return get(m, key) != NULL; +} + +void *map_get(const struct map *m, const char *key) +{ + struct map_entry *e = get(m, key); + void *value = NULL; + if(e != NULL) + { + value = e->value; + } + return value; +} + +void map_put_all(struct map *m, const struct map *source) +{ + struct map_iterator i; + const struct map_entry *e; + for(map_get_iterator(source, &i); (e = map_iterator_next(&i)) != NULL;) + { + map_put(m, e->key, e->value); + } +} + +int map_contains(const struct map *m1, const struct map *m2, cb_cmp *f) +{ + struct map_iterator i; + const struct map_entry *e; + + for(map_get_iterator(m2, &i); (e = map_iterator_next(&i)) != NULL;) + { + int pos; + pos = vec_find(&m1->vec, map_entry_cmp, e); + if(pos == -1) + { + /* error, find failed */ + LOG(LOG_ERROR, ("map_put: vec_find() internal error\n")); + exit(1); + } + if(pos < 0) + { + /* not found, break */ + break; + } + else if(f != NULL) + { + struct map_entry *e2; + /* compare the values too */ + e2 = vec_get(&m1->vec, pos); + if(f(e2->value, e->value)) + { + /* values not equal, break */ + break; + } + } + } + return e == NULL; +} + +int map_equals(const struct map *m1, const struct map *m2, cb_cmp *f) +{ + return map_contains(m1, m2, f) && map_contains(m2, m1, f); +} + +void map_get_iterator(const struct map *m, struct map_iterator *i) +{ + vec_get_iterator(&m->vec, &i->vec); +} + +const struct map_entry *map_iterator_next(struct map_iterator *i) +{ + return vec_iterator_next(&i->vec); +} diff --git a/loader/tools/exomizer-3.1/src/map.h b/loader/tools/exomizer-3.1/src/map.h new file mode 100644 index 0000000..a1b16b5 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/map.h @@ -0,0 +1,75 @@ +#ifndef ALREADY_INCLUDED_MAP +#define ALREADY_INCLUDED_MAP +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2006 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "vec.h" +#include "callback.h" + +struct map_entry { + const char *key; + void *value; +}; + +#define STATIC_MAP_INIT {STATIC_VEC_INIT(sizeof(struct map_entry))} + +struct map { + struct vec vec; +}; + +struct map_iterator { + struct vec_iterator vec; +}; + +void map_init(struct map *m); +void map_clear(struct map *m); +void map_free(struct map *m); + +void *map_put(struct map *m, const char *key, void *value); +void *map_get(const struct map *m, const char *key); +int map_contains_key(const struct map *m, const char *key); +void map_put_all(struct map *m, const struct map *source); + +int map_contains(const struct map *m1, const struct map *m2, cb_cmp *f); + +/** + * If f is NULL, only the keys will be compared. + * returns -1 on error, 1 on equality and 0 otherwise, + **/ +int map_equals(const struct map *m1, const struct map *m2, cb_cmp *f); + +void map_get_iterator(const struct map *p, struct map_iterator *i); +const struct map_entry *map_iterator_next(struct map_iterator *i); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/match.c b/loader/tools/exomizer-3.1/src/match.c new file mode 100644 index 0000000..da6ca5e --- /dev/null +++ b/loader/tools/exomizer-3.1/src/match.c @@ -0,0 +1,602 @@ +/* + * Copyright (c) 2002 - 2005, 2013 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include +#include "log.h" +#include "match.h" +#include "chunkpool.h" +#include "radix.h" +#include "buf.h" +#include "progress.h" + +struct match_node { + int index; + struct match_node *next; +}; + +static const struct match *matches_calc(struct match_ctx *ctx, /* IN/OUT */ + int index, /* IN */ + int favor_speed); /* IN */ + +struct match *match_new(struct match_ctx *ctx, /* IN/OUT */ + struct match **mpp, + int len, + int offset) +{ + struct match *m = chunkpool_malloc(&ctx->m_pool); + + if(len == 0) + { + LOG(LOG_ERROR, ("tried to allocate len0 match.\n")); + *(volatile int*)0; + } + if(len > ctx->max_len) + { + len = ctx->max_len; + } + + m->len = len; + m->offset = offset; + + /* insert new node in list */ + m->next = *mpp; + *mpp = m; + + return m; +} + +#define IS_READABLE(NOREAD, OFFSET) \ + ((NOREAD) == NULL || (NOREAD)[OFFSET] == 0) + +#define READ_BUF(IN, NOREAD, OFFSET) \ + ((NOREAD) != NULL && (NOREAD)[OFFSET] != 0 ? -1 : IN[OFFSET]) + +void match_ctx_init(struct match_ctx *ctx, /* IN/OUT */ + const struct buf *inbuf, /* IN */ + const struct buf *noread_inbuf, /* IN */ + int max_len, /* IN */ + int max_offset, /* IN */ + int favor_speed) /* IN */ +{ + struct match_node *np; + struct progress prog; + + int buf_len = buf_size(inbuf); + const unsigned char *buf = buf_data(inbuf); + const unsigned char *noread_buf = NULL; + char *rle_map; + + int c, i; + int val; + + if (noread_inbuf != NULL) + { + noread_buf = buf_data(noread_inbuf); + } + + ctx->info = calloc(buf_len + 1, sizeof(*ctx->info)); + ctx->rle = calloc(buf_len + 1, sizeof(*ctx->rle)); + ctx->rle_r = calloc(buf_len + 1, sizeof(*ctx->rle_r)); + + chunkpool_init(&ctx->m_pool, sizeof(struct match)); + + ctx->max_offset = max_offset; + ctx->max_len = max_len; + + ctx->buf = buf; + ctx->len = buf_len; + ctx->noread_buf = noread_buf; + + val = READ_BUF(buf, noread_buf, 0); + for (i = 1; i < buf_len; ++i) + { + if (val != -1 && buf[i] == val) + { + int len = ctx->rle[i - 1] + 1; + if(len > ctx->max_len) + { + len = ctx->max_len; + } + ctx->rle[i] = len; + } else + { + ctx->rle[i] = 0; + } + val = READ_BUF(buf, noread_buf, i); + } + + val = READ_BUF(buf, noread_buf, 0); + for (i = buf_len - 2; i >= 0; --i) + { + if (val != -1 && buf[i] == val) + { + int len = ctx->rle_r[i + 1] + 1; + if(len > ctx->max_len) + { + len = ctx->max_len; + } + ctx->rle_r[i] = len; + } else + { + ctx->rle_r[i] = 0; + } + val = READ_BUF(buf, noread_buf, i); + } + + /* add extra nodes to rle sequences */ + rle_map = malloc(65536); + for(c = 0; c < 256; ++c) + { + struct match_node *prev_np; + struct match_node *trailing_np; + unsigned short int rle_len; + + /* for each possible rle char */ + memset(rle_map, 0, 65536); + prev_np = NULL; + trailing_np = NULL; + for (i = 0; i < buf_len; ++i) + { + /* must be the correct char */ + if(buf[i] != c) + { + continue; + } + + rle_len = ctx->rle[i]; + if(!rle_map[rle_len] && ctx->rle_r[i] > 16) + { + /* no previous lengths and not our primary length*/ + continue; + } + + if (favor_speed && + ctx->rle_r[i] != 0 && ctx->rle[i] != 0) + { + continue; + } + + np = chunkpool_malloc(&ctx->m_pool); + np->index = i; + np->next = NULL; + rle_map[rle_len] = 1; + + LOG(LOG_DUMP, ("0) c = %d, added np idx %d -> %d\n", c, i, 0)); + + /* if we have a previous entry, let's chain it together */ + if(prev_np != NULL) + { + LOG(LOG_DUMP, ("1) c = %d, pointed np idx %d -> %d\n", + c, prev_np->index, i)); + prev_np->next = np; + + if (IS_READABLE(noread_buf, prev_np->index)) + { + trailing_np = prev_np; + } + } + + if (trailing_np != NULL && IS_READABLE(noread_buf, np->index)) + { + while(trailing_np != prev_np) + { + struct match_node *tmp = trailing_np->next; + trailing_np->next = np; + trailing_np = tmp; + } + trailing_np = NULL; + } + + ctx->info[i].single = np; + prev_np = np; + } + while(trailing_np != NULL) + { + struct match_node *tmp = trailing_np->next; + trailing_np->next = NULL; + trailing_np = tmp; + } + + memset(rle_map, 0, 65536); + prev_np = NULL; + for (i = buf_len - 1; i >= 0; --i) + { + /* must be the correct char */ + if(buf[i] != c) + { + continue; + } + + rle_len = ctx->rle_r[i]; + np = ctx->info[i].single; + if(np == NULL) + { + if(rle_map[rle_len] && prev_np != NULL && rle_len > 0) + { + np = chunkpool_malloc(&ctx->m_pool); + np->index = i; + np->next = prev_np; + ctx->info[i].single = np; + + LOG(LOG_DEBUG, ("2) c = %d, added np idx %d -> %d\n", + c, i, prev_np->index)); + } + } + else if (IS_READABLE(noread_buf, np->index)) + { + prev_np = np; + } + + if(ctx->rle_r[i] > 0) + { + continue; + } + rle_len = ctx->rle[i] + 1; + rle_map[rle_len] = 1; + } + } + free(rle_map); + + progress_init(&prog, "building.directed.acyclic.graph.", buf_len - 1, 0); + + for (i = buf_len - 1; i >= 0; --i) + { + const struct match *matches; + + /* let's populate the cache */ + matches = matches_calc(ctx, i, favor_speed); + + /* add to cache */ + ctx->info[i].cache = matches; + + progress_bump(&prog, i); + } + + progress_free(&prog); +} + +void match_ctx_free(struct match_ctx *ctx) /* IN/OUT */ +{ + chunkpool_free(&ctx->m_pool); + free(ctx->info); + free(ctx->rle); + free(ctx->rle_r); +} + +void dump_matches(int level, const struct match *mp) +{ + if (mp == NULL) + { + LOG(level, (" (NULL)\n")); + } else + { + if(mp->offset > 0) + { + LOG(level, (" offset %d, len %d\n", mp->offset, mp->len)); + } + if (mp->next != NULL) + { + dump_matches(level, mp->next); + } + } +} + +const struct match *matches_get(const struct match_ctx *ctx, /* IN/OUT */ + int index) /* IN */ +{ + return ctx->info[index].cache; +} + +/* this needs to be called with the indexes in + * reverse order */ +const struct match *matches_calc(struct match_ctx *ctx, /* IN/OUT */ + int index, /* IN */ + int favor_speed) /* IN */ +{ + const unsigned char *buf; + const unsigned char *noread_buf; + + struct match *matches; + struct match *mp; + const struct match_node *np; + + buf = ctx->buf; + noread_buf = ctx->noread_buf; + matches = NULL; + + LOG(LOG_DUMP, ("index %d, char '%c', rle %d, rle_r %d\n", + index, buf[index], ctx->rle[index], + ctx->rle_r[index])); + + /* proces the literal match and add it to matches */ + mp = match_new(ctx, &matches, 1, 0); + + /* get possible match */ + np = ctx->info[index].single; + if(np != NULL) + { + np = np->next; + } + for (; np != NULL; np = np->next) + { + int mp_len; + int len; + int pos; + int offset; + + /* limit according to max offset */ + if(np->index > index + ctx->max_offset) + { + break; + } + + LOG(LOG_DUMP, ("find lengths for index %d to index %d\n", + index, np->index)); + + /* get match len */ + mp_len = mp->offset > 0 ? mp->len : 0; + LOG(LOG_DUMP, ("0) comparing with current best [%d] off %d len %d\n", + index, mp->offset, mp_len)); + + offset = np->index - index; + len = mp_len; + pos = index + 1 - len; + /* Compare the first bytes backwards. We can + * skip some comparisons by increasing by the rle count. We + * don't need to compare the first byte, hence > 1 instead of + * > 0 */ + while(len > 1 && buf[pos] == READ_BUF(buf, noread_buf, pos + offset)) + { + int offset1 = ctx->rle_r[pos]; + int offset2 = ctx->rle_r[pos + offset]; + int offset = offset1 < offset2 ? offset1 : offset2; + + LOG(LOG_DUMP, ("1) compared sucesssfully [%d] %d %d\n", + index, pos, pos + offset)); + + len -= 1 + offset; + pos += 1 + offset; + } + if(len > 1) + { + /* sequence length too short, skip this match */ + continue; + } + + if(offset < 17) + { + /* allocate match struct and add it to matches */ + mp = match_new(ctx, &matches, 1, offset); + } + + /* Here we know that the current match is atleast as long as + * the previous one. let's compare further. */ + len = mp_len; + pos = index - len; + while(len <= ctx->max_len && + pos >= 0 && buf[pos] == READ_BUF(buf, noread_buf, pos + offset)) + { + LOG(LOG_DUMP, ("2) compared sucesssfully [%d] %d %d\n", + index, pos, pos + offset)); + ++len; + --pos; + } + if(len > mp_len || (!favor_speed && len == mp_len)) + { + /* allocate match struct and add it to matches */ + mp = match_new(ctx, &matches, index - pos, offset); + } + if (len > ctx->max_len) + { + break; + } + if(pos < 0) + { + /* we have reached the eof, no better matches can be found */ + break; + } + } + LOG(LOG_DEBUG, ("adding matches for index %d to cache\n", index)); + dump_matches(LOG_DEBUG, matches); + + return matches; +} + +static int match_keep_this(const struct match *mp) +{ + int val = 1; + /* if we want to ignore this match then return true else false */ + if(mp->len == 1) + { + if(mp->offset > 34) + { + val = 0; + } + } + return val; +} + +static void match_cache_peek(const struct match_ctx *ctx, + int pos, + const struct match **litpp, + const struct match **seqpp, + struct match *lit_tmp, + struct match *val_tmp) +{ + const struct match *litp, *seqp, *val; + + seqp = NULL; + litp = NULL; + if(pos >= 0) + { + val = matches_get(ctx, pos); + litp = val; + while(litp->offset != 0) + { + litp = litp->next; + } + + /* inject extra rle match */ + if(ctx->rle_r[pos] > 0 && ctx->rle[pos + 1] > 0) + { + val_tmp->offset = 1; + val_tmp->len = ctx->rle[pos + 1]; + val_tmp->next = (struct match *)val; + val = val_tmp; + LOG(LOG_DEBUG, ("injecting rle val(%d,%d)\n", + val->len, val->offset)); + } + + while(val != NULL) + { + if(val->offset != 0) + { + if(match_keep_this(val)) + { + if(seqp == NULL || val->len > seqp->len || + (val->len == seqp->len && val->offset < seqp->offset)) + { + seqp = val; + } + } + if(litp->offset == 0 || litp->offset > val->offset) + { + LOG(LOG_DEBUG, ("val(%d,%d)", val->len, val->offset)); + if(lit_tmp != NULL) + { + int diff; + struct match tmp2; + tmp2 = *val; + tmp2.len = 1; + diff = ctx->rle[pos + val->offset]; + if(tmp2.offset > diff) + { + tmp2.offset -= diff; + } + else + { + tmp2.offset = 1; + } + LOG(LOG_DEBUG, ("=> litp(%d,%d)", + tmp2.len, tmp2.offset)); + if(match_keep_this(&tmp2)) + { + LOG(LOG_DEBUG, (", keeping")); + *lit_tmp = tmp2; + litp = lit_tmp; + } + } + LOG(LOG_DEBUG, ("\n")); + } + } + val = val->next; + } + } + + if(litpp != NULL) *litpp = litp; + if(seqpp != NULL) *seqpp = seqp; +} + +void match_cache_get_enum(struct match_ctx *ctx, /* IN */ + struct match_cache_enum *mpce) /* IN/OUT */ +{ + mpce->ctx = ctx; + mpce->pos = ctx->len - 1; +} + +const struct match *match_cache_enum_get_next(void *match_cache_enum) +{ + const struct match *val, *lit, *seq; + struct match_cache_enum *mpce; + + mpce = match_cache_enum; + + match_cache_peek(mpce->ctx, mpce->pos, &lit, &seq, + &mpce->tmp1, &mpce->tmp2); + + val = lit; + if(lit == NULL) + { + /* the end, reset enum and return NULL */ + mpce->pos = mpce->ctx->len - 1; + } + else + { + if(seq != NULL) + { + struct match t1; + struct match t2; + const struct match *next; + match_cache_peek(mpce->ctx, mpce->pos - 1, NULL, &next, &t1, &t2); + if(next == NULL || + (seq->len >= next->len + ((mpce->pos & 1) && next->len < 3))) + { + /* nope, next is not better, use this sequence */ + val = seq; + } + } + } + if(val != NULL) + { + LOG(LOG_DEBUG, ("Using len %05d, offset, %05d\n", val->len, val->offset)); + mpce->pos -= val->len; + } + return val; +} + +void match_concat_get_enum(match_enum_next_f *next_f, + struct vec *data_vec, + struct match_concat_enum *mpcce) +{ + vec_get_iterator(data_vec, &mpcce->enum_iterator); + mpcce->next_f = next_f; + mpcce->enum_current = vec_iterator_next(&mpcce->enum_iterator); +} + +const struct match *match_concat_enum_get_next(void *match_concat_enum) /*IN*/ +{ + struct match_concat_enum *e = match_concat_enum; + const struct match *mp = NULL; +restart: + if (e->enum_current == NULL) + { + e->enum_current = vec_iterator_next(&e->enum_iterator); + } + else + { + mp = e->next_f(e->enum_current); + if (mp == NULL) + { + e->enum_current = vec_iterator_next(&e->enum_iterator); + goto restart; + } + } + return mp; +} diff --git a/loader/tools/exomizer-3.1/src/match.h b/loader/tools/exomizer-3.1/src/match.h new file mode 100644 index 0000000..d9344ec --- /dev/null +++ b/loader/tools/exomizer-3.1/src/match.h @@ -0,0 +1,107 @@ +#ifndef ALREADY_INCLUDED_MATCH +#define ALREADY_INCLUDED_MATCH +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002 - 2005, 2013 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "chunkpool.h" +#include "buf.h" +#include "vec.h" + +struct match { + unsigned int offset; + unsigned short int len; + const struct match *next; +}; + +struct pre_calc { + struct match_node *single; + const struct match *cache; +}; + +struct match_ctx { + struct chunkpool m_pool; + struct pre_calc *info; + unsigned short int *rle; + unsigned short int *rle_r; + const unsigned char *buf; + const unsigned char *noread_buf; + int len; + int max_offset; + int max_len; +}; + +void match_ctx_init(struct match_ctx *ctx, /* IN/OUT */ + const struct buf *inbuf, /* IN */ + const struct buf *noread_inbuf, /* IN */ + int max_len, /* IN */ + int max_offset, /* IN */ + int favor_speed); /* IN */ + +void match_ctx_free(struct match_ctx *ctx); /* IN/OUT */ + +/* this needs to be called with the indexes in + * reverse order */ +const struct match *matches_get(const struct match_ctx *ctx, /* IN/OUT */ + int index); /* IN */ + +void match_delete(struct match_ctx *ctx, /* IN/OUT */ + struct match *mp); /* IN */ + +struct match_cache_enum { + const struct match_ctx *ctx; + struct match tmp1; + struct match tmp2; + int pos; +}; + +typedef const struct match *match_enum_next_f(void *match_enum_data); + +void match_cache_get_enum(struct match_ctx *ctx, /* IN */ + struct match_cache_enum *mpce); /* IN/OUT */ + +const struct match *match_cache_enum_get_next(void *match_cache_enum); /* IN */ + +struct match_concat_enum { + struct vec_iterator enum_iterator; + match_enum_next_f *next_f; + void *enum_current; +}; + +void match_concat_get_enum(match_enum_next_f *next_f, + struct vec *data_vec, + struct match_concat_enum *mpcce); + +const struct match *match_concat_enum_get_next(void *match_concat_enum); /*IN*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/named_buffer.c b/loader/tools/exomizer-3.1/src/named_buffer.c new file mode 100644 index 0000000..29f6217 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/named_buffer.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "named_buffer.h" +#include "log.h" +#include "chunkpool.h" +#include "buf_io.h" + +#include + +void named_buffer_init(struct named_buffer *nb) +{ + map_init(&nb->map); + chunkpool_init(&nb->buf, sizeof(struct buf)); +} + +void named_buffer_free(struct named_buffer *nb) +{ + typedef void cb_free(void *a); + + chunkpool_free2(&nb->buf, (cb_free*)buf_free); + map_free(&nb->map); +} + +void named_buffer_clear(struct named_buffer *nb) +{ + named_buffer_free(nb); + named_buffer_init(nb); +} + +void named_buffer_copy(struct named_buffer *nb, + const struct named_buffer *source) +{ + struct map_iterator i; + const struct map_entry *e; + + for(map_get_iterator(&source->map, &i); + (e = map_iterator_next(&i)) != NULL;) + { + /* don't allocate copies of the entries */ + map_put(&nb->map, e->key, e->value); + } +} + +struct buf *new_named_buffer(struct named_buffer *nb, const char *name) +{ + struct buf *mp; + + mp = chunkpool_malloc(&nb->buf); + /* name is already strdup:ped */ + if(map_put(&nb->map, name, mp) != NULL) + { + /* found */ + LOG(LOG_ERROR, ("buffer already exists.\n")); + exit(1); + } + buf_init(mp); + return mp; +} + +struct buf *get_named_buffer(struct named_buffer *nb, const char *name) +{ + struct buf *mp; + + mp = map_get(&nb->map, name); + if(mp == NULL) + { + mp = new_named_buffer(nb, name); + } + + return mp; +} diff --git a/loader/tools/exomizer-3.1/src/named_buffer.h b/loader/tools/exomizer-3.1/src/named_buffer.h new file mode 100644 index 0000000..4cc60cb --- /dev/null +++ b/loader/tools/exomizer-3.1/src/named_buffer.h @@ -0,0 +1,56 @@ +#ifndef NAMED_BUFFER_INCLUDED +#define NAMED_BUFFER_INCLUDED +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "buf.h" +#include "chunkpool.h" +#include "map.h" + +struct named_buffer { + struct map map; + struct chunkpool buf; +}; + +void named_buffer_init(struct named_buffer *nb); +void named_buffer_free(struct named_buffer *nb); +void named_buffer_clear(struct named_buffer *nb); + +void named_buffer_copy(struct named_buffer *nb, + const struct named_buffer *source); + +struct buf *new_named_buffer(struct named_buffer *nb, const char *name); +struct buf *get_named_buffer(struct named_buffer *nb, const char *name); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/optimal.c b/loader/tools/exomizer-3.1/src/optimal.c new file mode 100644 index 0000000..e2e4064 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/optimal.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2002 - 2007 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "search.h" +#include "radix.h" +#include "chunkpool.h" +#include "optimal.h" + +struct interval_node { + int start; + int score; + struct interval_node *next; + signed char prefix; + signed char bits; + signed char depth; + signed char flags; +}; + +static +void +interval_node_init(struct interval_node *inp, int start, int depth, int flags) +{ + inp->start = start; + inp->flags = flags; + inp->depth = depth; + inp->bits = 0; + inp->prefix = flags >= 0 ? flags : depth + 1; + inp->score = -1; + inp->next = NULL; +} + +static struct interval_node *interval_node_clone(struct interval_node *inp) +{ + struct interval_node *inp2 = NULL; + + if(inp != NULL) + { + inp2 = malloc(sizeof(struct interval_node)); + if (inp2 == NULL) + { + LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n", + __FILE__, __LINE__)); + exit(0); + } + /* copy contents */ + *inp2 = *inp; + inp2->next = interval_node_clone(inp->next); + } + + return inp2; +} + +static void interval_node_delete(struct interval_node *inp) +{ + struct interval_node *inp2; + while (inp != NULL) + { + inp2 = inp; + inp = inp->next; + free(inp2); + } +} + +static void interval_node_dump(int level, struct interval_node *inp) +{ + int end; + + end = 0; + while (inp != NULL) + { + end = inp->start + (1 << inp->bits); + LOG(level, ("%X", inp->bits)); + inp = inp->next; + } + LOG(level, ("[eol@%d]\n", end)); +} + +float optimal_encode_int(int arg, void *priv, struct output_ctx *out, + struct encode_int_bucket *eibp) +{ + struct interval_node *inp; + int end; + + float val; + + inp = priv; + val = 100000000.0; + end = 0; + while (inp != NULL) + { + end = inp->start + (1 << inp->bits); + if (arg >= inp->start && arg < end) + { + break; + } + inp = inp->next; + } + if (inp != NULL) + { + val = (float) (inp->prefix + inp->bits); + if (eibp != NULL) + { + eibp->start = inp->start; + eibp->end = end; + } + } else + { + val += (float) (arg - end); + if (eibp != NULL) + { + eibp->start = 0; + eibp->end = 0; + } + } + LOG(LOG_DUMP, ("encoding %d to %0.1f bits\n", arg, val)); + + if (out != NULL) + { + output_bits(out, inp->bits, arg - inp->start); + if (inp->flags < 0) + { + LOG(LOG_DUMP, ("gamma prefix code = %d\n", inp->depth)); + output_gamma_code(out, inp->depth); + } else + { + LOG(LOG_DUMP, ("flat prefix %d bits\n", inp->depth)); + output_bits(out, inp->prefix, inp->depth); + } + } + + return val; +} + +float optimal_encode(const struct match *mp, + struct encode_match_data *emd, + unsigned int prev_offset, + struct encode_match_buckets *embp) +{ + struct interval_node **offset; + float bits; + struct encode_match_priv *data; + struct encode_int_bucket *eib_len = NULL; + struct encode_int_bucket *eib_offset = NULL; + if (embp != NULL) + { + eib_len = &embp->len; + eib_offset = &embp->offset; + } + + data = emd->priv; + offset = data->offset_f_priv; + + bits = 0.0; + if (mp->len > 255 && (data->flags_notrait & TFLAG_LEN0123_SEQ_MIRRORS) && + (mp->len & 255) < ((data->flags_proto & PFLAG_4_OFFSET_TABLES) + ? 4 : 3)) + { + bits += 100000000.0; + } + if (mp->offset == 0) + { + bits += 9.0f * mp->len; + data->lit_num += mp->len; + data->lit_bits += bits; + } else + { + bits += 1.0; + if (mp->offset != prev_offset) + { + switch (mp->len) + { + case 0: + LOG(LOG_ERROR, ("bad len\n")); + exit(1); + break; + case 1: + if (data->flags_notrait & TFLAG_LEN1_SEQ) + { + bits += 100000000.0; + } + else + { + bits += data->offset_f(mp->offset, offset[0], emd->out, + eib_offset); + } + break; + case 2: + bits += data->offset_f(mp->offset, offset[1], emd->out, + eib_offset); + break; + case 3: + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + bits += data->offset_f(mp->offset, offset[2], emd->out, + eib_offset); + break; + } + default: + bits += data->offset_f(mp->offset, offset[7], emd->out, + eib_offset); + break; + } + } + if (prev_offset > 0) + { + /* reuse previous offset state cost */ + bits += 1.0; + if (emd->out != NULL) + { + output_bits(emd->out, 1, mp->offset == prev_offset); + } + } + bits += data->len_f(mp->len, data->len_f_priv, emd->out, eib_len); + if (bits > (9.0 * mp->len)) + { + /* lets make literals out of it */ + data->lit_num += 1; + data->lit_bits += bits; + } else + { + if (mp->offset == 1) + { + data->rle_num += 1; + data->rle_bits += bits; + } else + { + data->seq_num += 1; + data->seq_bits += bits; + } + } + } + if (embp != NULL) + { + if (eib_len->start + eib_len->end == 0 || + eib_offset->start + eib_offset->end == 0) + { + eib_len->start = 0; + eib_len->end = 0; + eib_offset->start = 0; + eib_offset->end = 0; + } + } + return bits; +} + +struct optimize_arg { + struct radix_root cache; + int *stats; + int *stats2; + int max_depth; + int flags; + struct chunkpool in_pool; +}; + +#define CACHE_KEY(START, DEPTH) ((int)((START)*32+DEPTH)) + +static struct interval_node *optimize1(struct optimize_arg *arg, int start, + int depth, int init) +{ + struct interval_node in; + struct interval_node *best_inp; + int key; + int end, i; + int start_count, end_count; + + LOG(LOG_DUMP, ("IN start %d, depth %d\n", start, depth)); + + do + { + best_inp = NULL; + if (arg->stats[start] == 0) + { + break; + } + key = CACHE_KEY(start, depth); + best_inp = radix_node_get(&arg->cache, key); + if (best_inp != NULL) + { + break; + } + + interval_node_init(&in, start, depth, arg->flags); + + for (i = 0; i < 16; ++i) + { + in.next = NULL; + in.bits = i; + end = start + (1 << i); + + start_count = end_count = 0; + if (start < 1000000) + { + start_count = arg->stats[start]; + if (end < 1000000) + { + end_count = arg->stats[end]; + } + } + + in.score = (start_count - end_count) * + (in.prefix + in.bits); + + /* one index below */ + LOG(LOG_DUMP, ("interval score: [%d<<%d[%d\n", + start, i, in.score)); + if (end_count > 0) + { + int penalty; + /* we're not done, now choose between using + * more bits, go deeper or skip the rest */ + if (depth + 1 < arg->max_depth) + { + /* we can go deeper, let's try that */ + in.next = optimize1(arg, end, depth + 1, i); + } + /* get the penalty for skipping */ + penalty = 100000000; + if (arg->stats2 != NULL) + { + penalty = arg->stats2[end]; + } + if (in.next != NULL && in.next->score < penalty) + { + penalty = in.next->score; + } + in.score += penalty; + } + if (best_inp == NULL || in.score < best_inp->score) + { + /* it's the new best in town, use it */ + if (best_inp == NULL) + { + /* allocate if null */ + best_inp = chunkpool_malloc(&arg->in_pool); + } + *best_inp = in; + } + } + if (best_inp != NULL) + { + radix_node_set(&arg->cache, key, best_inp); + } + } + while (0); + + if(IS_LOGGABLE(LOG_DUMP)) + { + LOG(LOG_DUMP, ("OUT depth %d: ", depth)); + interval_node_dump(LOG_DUMP, best_inp); + } + return best_inp; +} + +static struct interval_node *optimize(int stats[], int stats2[], + int max_depth, int flags) +{ + struct optimize_arg arg; + + struct interval_node *inp; + + arg.stats = stats; + arg.stats2 = stats2; + + arg.max_depth = max_depth; + arg.flags = flags; + + chunkpool_init(&arg.in_pool, sizeof(struct interval_node)); + + radix_tree_init(&arg.cache); + + inp = optimize1(&arg, 1, 0, 0); + + /* use normal malloc for the winner */ + inp = interval_node_clone(inp); + + /* cleanup */ + radix_tree_free(&arg.cache, NULL, NULL); + chunkpool_free(&arg.in_pool); + + return inp; +} + +static void export_helper(struct interval_node *np, int depth, + struct buf *target) +{ + while(np != NULL) + { + buf_printf(target, "%X", np->bits); + np = np->next; + --depth; + } + while(depth-- > 0) + { + buf_printf(target, "0"); + } +} + + +void optimal_encoding_export(struct encode_match_data *emd, + struct buf *target) +{ + struct interval_node **offsets; + struct encode_match_priv *data; + + buf_clear(target); + data = emd->priv; + offsets = data->offset_f_priv; + export_helper(data->len_f_priv, 16, target); + buf_append_char(target, ','); + export_helper(offsets[0], 4, target); + buf_append_char(target, ','); + export_helper(offsets[1], 16, target); + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + buf_append_char(target, ','); + export_helper(offsets[2], 16, target); + } + buf_append_char(target, ','); + export_helper(offsets[7], 16, target); +} + +static void import_helper(struct interval_node **npp, + const char **encodingp, + int flags) +{ + int c; + int start = 1; + int depth = 0; + const char *encoding; + + encoding = *encodingp; + while((c = *(encoding++)) != '\0') + { + char buf[2] = {0, 0}; + char *dummy; + int bits; + struct interval_node *np; + + if(c == ',') + { + break; + } + + buf[0] = c; + bits = strtol(buf, &dummy, 16); + + LOG(LOG_DUMP, ("got bits %d\n", bits)); + + np = malloc(sizeof(struct interval_node)); + interval_node_init(np, start, depth, flags); + np->bits = bits; + + ++depth; + start += 1 << bits; + + *npp = np; + npp = &(np->next); + } + *encodingp = encoding; +} + +void optimal_encoding_import(struct encode_match_data *emd, + const char *encoding) +{ + struct encode_match_priv *data; + struct interval_node **npp, **offsets; + + LOG(LOG_DEBUG, ("importing encoding: %s\n", encoding)); + + data = emd->priv; + + optimal_free(emd); + optimal_init(emd, data->flags_notrait, data->flags_proto); + + data = emd->priv; + offsets = data->offset_f_priv; + + /* lengths */ + npp = (void*)&data->len_f_priv; + import_helper(npp, &encoding, -1); + + /* offsets, len = 1 */ + npp = &offsets[0]; + import_helper(npp, &encoding, 2); + + /* offsets, len = 2 */ + npp = &offsets[1]; + import_helper(npp, &encoding, 4); + + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + /* offsets, len = 3 */ + npp = &offsets[2]; + import_helper(npp, &encoding, 4); + } + + /* offsets, len >= 3 */ + npp = &offsets[7]; + import_helper(npp, &encoding, 4); + + LOG(LOG_DEBUG, ("imported encoding: ")); + optimal_dump(LOG_DEBUG, emd); +} + +void optimal_init(struct encode_match_data *emd,/* IN/OUT */ + int flags_notrait, /* IN */ + int flags_proto) /* IN */ +{ + struct encode_match_priv *data; + struct interval_node **inpp; + + emd->priv = malloc(sizeof(struct encode_match_priv)); + data = emd->priv; + + memset(data, 0, sizeof(struct encode_match_priv)); + + data->offset_f = optimal_encode_int; + data->len_f = optimal_encode_int; + inpp = malloc(sizeof(struct interval_node *) * 8); + inpp[0] = NULL; + inpp[1] = NULL; + inpp[2] = NULL; + inpp[3] = NULL; + inpp[4] = NULL; + inpp[5] = NULL; + inpp[6] = NULL; + inpp[7] = NULL; + data->offset_f_priv = inpp; + data->len_f_priv = NULL; + data->flags_notrait = flags_notrait; + data->flags_proto = flags_proto; +} + +void optimal_free(struct encode_match_data *emd) /* IN */ +{ + struct encode_match_priv *data; + struct interval_node **inpp; + struct interval_node *inp; + + data = emd->priv; + + inpp = data->offset_f_priv; + if (inpp != NULL) + { + interval_node_delete(inpp[0]); + interval_node_delete(inpp[1]); + interval_node_delete(inpp[2]); + interval_node_delete(inpp[3]); + interval_node_delete(inpp[4]); + interval_node_delete(inpp[5]); + interval_node_delete(inpp[6]); + interval_node_delete(inpp[7]); + } + free(inpp); + + inp = data->len_f_priv; + interval_node_delete(inp); + + data->offset_f_priv = NULL; + data->len_f_priv = NULL; +} + +void freq_stats_dump(int level, int arr[]) +{ + int i; + for (i = 0; i < 32; ++i) + { + LOG(level, ("%d, ", arr[i] - arr[i + 1])); + } + LOG(level, ("\n")); +} + +void freq_stats_dump_raw(int level, int arr[]) +{ + int i; + for (i = 0; i < 32; ++i) + { + LOG(level, ("%d, ", arr[i])); + } + LOG(level, ("\n")); +} + +void optimal_optimize(struct encode_match_data *emd, /* IN/OUT */ + match_enum_next_f *enum_next_f, /* IN */ + void *matchp_enum) /* IN */ +{ + struct encode_match_priv *data; + const struct match *mp; + struct interval_node **offset; + static int offset_arr[8][1000000]; + static int offset_parr[8][1000000]; + static int len_arr[1000000]; + int treshold; + + int i, j; + + data = emd->priv; + + memset(offset_arr, 0, sizeof(offset_arr)); + memset(offset_parr, 0, sizeof(offset_parr)); + memset(len_arr, 0, sizeof(len_arr)); + + offset = data->offset_f_priv; + + /* first the lens */ + while ((mp = enum_next_f(matchp_enum)) != NULL) + { + if (mp->offset > 0) + { + len_arr[mp->len] += 1; + if(len_arr[mp->len] < 0) + { + LOG(LOG_ERROR, ("len counter wrapped!\n")); + } + } + } + + for (i = 65534; i >= 0; --i) + { + len_arr[i] += len_arr[i + 1]; + if(len_arr[i] < 0) + { + LOG(LOG_ERROR, ("len counter wrapped!\n")); + } + } + + data->len_f_priv = optimize(len_arr, NULL, 16, -1); + + /* then the offsets */ + while ((mp = enum_next_f(matchp_enum)) != NULL) + { + if (mp->offset > 0) + { + treshold = mp->len * 9; + treshold -= 1 + (int) optimal_encode_int(mp->len, + data->len_f_priv, + NULL, NULL); + switch (mp->len) + { + case 0: + LOG(LOG_ERROR, ("bad len\n")); + exit(0); + break; + case 1: + offset_parr[0][mp->offset] += treshold; + offset_arr[0][mp->offset] += 1; + if(offset_arr[0][mp->offset] < 0) + { + LOG(LOG_ERROR, ("offset0 counter wrapped!\n")); + } + break; + case 2: + offset_parr[1][mp->offset] += treshold; + offset_arr[1][mp->offset] += 1; + if(offset_arr[1][mp->offset] < 0) + { + LOG(LOG_ERROR, ("offset1 counter wrapped!\n")); + } + break; + case 3: + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + offset_parr[2][mp->offset] += treshold; + offset_arr[2][mp->offset] += 1; + if(offset_arr[2][mp->offset] < 0) + { + LOG(LOG_ERROR, ("offset2 counter wrapped!\n")); + } + break; + } + default: + offset_parr[7][mp->offset] += treshold; + offset_arr[7][mp->offset] += 1; + if(offset_arr[7][mp->offset] < 0) + { + LOG(LOG_ERROR, ("offset7 counter wrapped!\n")); + } + break; + } + } + } + + for (i = 999998; i >= 0; --i) + { + for (j = 0; j < 8; ++j) + { + offset_arr[j][i] += offset_arr[j][i + 1]; + offset_parr[j][i] += offset_parr[j][i + 1]; + } + } + + offset[0] = optimize(offset_arr[0], offset_parr[0], 1 << 2, 2); + offset[1] = optimize(offset_arr[1], offset_parr[1], 1 << 4, 4); + offset[2] = optimize(offset_arr[2], offset_parr[2], 1 << 4, 4); + offset[3] = optimize(offset_arr[3], offset_parr[3], 1 << 4, 4); + offset[4] = optimize(offset_arr[4], offset_parr[4], 1 << 4, 4); + offset[5] = optimize(offset_arr[5], offset_parr[5], 1 << 4, 4); + offset[6] = optimize(offset_arr[6], offset_parr[6], 1 << 4, 4); + offset[7] = optimize(offset_arr[7], offset_parr[7], 1 << 4, 4); + + if(IS_LOGGABLE(LOG_DEBUG)) + { + optimal_dump(LOG_DEBUG, emd); + } +} + +void optimal_dump(int level, struct encode_match_data *emd) +{ + struct encode_match_priv *data; + struct interval_node **offset; + struct interval_node *len; + + data = emd->priv; + + offset = data->offset_f_priv; + len = data->len_f_priv; + + LOG(level, ("lens: ")); + interval_node_dump(level, len); + + LOG(level, ("offsets (len =1): ")); + interval_node_dump(level, offset[0]); + + LOG(level, ("offsets (len =2): ")); + interval_node_dump(level, offset[1]); + + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + LOG(level, ("offsets (len =3): ")); + interval_node_dump(level, offset[2]); + } + LOG(level, ("offsets (len =8): ")); + interval_node_dump(level, offset[7]); +} + +static void interval_out(struct output_ctx *out, struct interval_node *inp1, + int size, int flags_proto) +{ + unsigned char buffer[256]; + unsigned char count; + struct interval_node *inp; + + count = 0; + + memset(buffer, 0, sizeof(buffer)); + inp = inp1; + while (inp != NULL) + { + ++count; + LOG(LOG_DUMP, ("bits %d, lo %d, hi %d\n", + inp->bits, inp->start & 0xFF, inp->start >> 8)); + buffer[sizeof(buffer) - count] = inp->bits; + inp = inp->next; + } + + while (size > 0) + { + int b; + b = buffer[sizeof(buffer) - size]; + LOG(LOG_DUMP, ("outputting nibble %d\n", b)); + if (flags_proto & PFLAG_BITS_COPY_GT_7) + { + output_bits(out, 1, b >> 3); + output_bits(out, 3, b & 7); + } + else + { + output_bits(out, 4, b); + } + size--; + } +} + +void optimal_out(struct output_ctx *out, /* IN/OUT */ + struct encode_match_data *emd) /* IN */ +{ + struct encode_match_priv *data; + struct interval_node **offset; + struct interval_node *len; + + data = emd->priv; + + offset = data->offset_f_priv; + len = data->len_f_priv; + + interval_out(out, offset[0], 4, data->flags_proto); + interval_out(out, offset[1], 16, data->flags_proto); + if (data->flags_proto & PFLAG_4_OFFSET_TABLES) + { + interval_out(out, offset[2], 16, data->flags_proto); + } + interval_out(out, offset[7], 16, data->flags_proto); + interval_out(out, len, 16, data->flags_proto); +} diff --git a/loader/tools/exomizer-3.1/src/optimal.h b/loader/tools/exomizer-3.1/src/optimal.h new file mode 100644 index 0000000..89085a8 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/optimal.h @@ -0,0 +1,66 @@ +#ifndef ALREADY_INCLUDED_OPTIMAL +#define ALREADY_INCLUDED_OPTIMAL +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "search.h" +#include "output.h" + +float optimal_encode(const struct match *mp, /* IN */ + struct encode_match_data *emp, /* IN */ + unsigned int prev_offset, /* IN */ + struct encode_match_buckets *embp);/* OUT */ + +void optimal_init(struct encode_match_data *emp, /* IN/OUT */ + int flags_notrait, /* IN */ + int flags_proto); /* IN */ + +void optimal_free(struct encode_match_data *emd); /* IN */ + +void optimal_optimize(struct encode_match_data *emd, /* IN/OUT */ + match_enum_next_f *enum_next_f, /* IN */ + void *priv); /* IN */ + +void optimal_encoding_import(struct encode_match_data *emd, /* IN/OUT */ + const char *encoding); /* IN */ + +void optimal_encoding_export(struct encode_match_data *emd, /* IN */ + struct buf *export); /* OUT */ + +void optimal_dump(int level, struct encode_match_data *emp); /* IN */ + +void optimal_out(struct output_ctx *out, /* IN/OUT */ + struct encode_match_data *emd); /* IN */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/output.c b/loader/tools/exomizer-3.1/src/output.c new file mode 100644 index 0000000..fc8c3ac --- /dev/null +++ b/loader/tools/exomizer-3.1/src/output.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2002 - 2007 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include "log.h" +#include "output.h" + +static void bitbuf_bit(struct output_ctx *ctx, int bit) +{ + if (ctx->flags_proto & PFLAG_BITS_ORDER_BE) + { + /* ror (new) */ + ctx->bitbuf >>= 1; + if (bit) + { + ctx->bitbuf |= 0x80; + } + if (++ctx->bitcount == 8) + { + output_bits_flush(ctx, 0); + } + } + else + { + /* rol (old) */ + ctx->bitbuf <<= 1; + if (bit) + { + ctx->bitbuf |= 0x01; + } + if (++ctx->bitcount == 8) + { + output_bits_flush(ctx, 0); + } + } +} + +void output_ctx_init(struct output_ctx *ctx, /* IN/OUT */ + int flags_proto, /* IN */ + struct buf *out)/* IN/OUT */ +{ + ctx->bitbuf = 0; + ctx->bitcount = 0; + ctx->pos = buf_size(out); + ctx->buf = out; + ctx->flags_proto = flags_proto; +} + +unsigned int output_get_pos(struct output_ctx *ctx) /* IN */ +{ + return ctx->pos; +} + +void output_byte(struct output_ctx *ctx, /* IN/OUT */ + unsigned char byte) /* IN */ +{ + /*LOG(LOG_DUMP, ("output_byte: $%02X\n", byte)); */ + if(ctx->pos < buf_size(ctx->buf)) + { + char *p; + p = buf_data(ctx->buf); + p[ctx->pos] = byte; + } + else + { + while(ctx->pos > buf_size(ctx->buf)) + { + buf_append_char(ctx->buf, '\0'); + } + buf_append_char(ctx->buf, byte); + } + ++(ctx->pos); +} + +void output_word(struct output_ctx *ctx, /* IN/OUT */ + unsigned short int word) /* IN */ +{ + output_byte(ctx, (unsigned char) (word & 0xff)); + output_byte(ctx, (unsigned char) (word >> 8)); +} + + +void output_bits_flush(struct output_ctx *ctx, /* IN/OUT */ + int add_marker_bit) /* IN */ +{ + if (add_marker_bit) + { + if (ctx->flags_proto & PFLAG_BITS_ORDER_BE) + { + ctx->bitbuf |= (0x80 >> ctx->bitcount); + } + else + { + ctx->bitbuf |= (0x01 << ctx->bitcount); + } + ++ctx->bitcount; + } + if (ctx->bitcount > 0) + { + /* flush the bitbuf */ + output_byte(ctx, ctx->bitbuf); + LOG(LOG_DUMP, ("bitstream flushed 0x%02X\n", ctx->bitbuf)); + /* reset it */ + ctx->bitbuf = 0; + ctx->bitcount = 0; + } +} + +int output_bits_alignment(struct output_ctx *ctx) +{ + int alignment = (8 - ctx->bitcount) & 7; + LOG(LOG_DUMP, ("bitbuf 0x%02X aligned %d\n", ctx->bitbuf, alignment)); + return alignment; +} + +static void output_bits_int(struct output_ctx *ctx, /* IN/OUT */ + int count, /* IN */ + int val) /* IN */ +{ + if (ctx->flags_proto & PFLAG_BITS_COPY_GT_7) + { + while (count > 7) + { + /* at least 8 bits or more are left */ + output_byte(ctx, (unsigned char)(val & 0xFF)); + count -= 8; + val >>= 8; + } + } + + while (count-- > 0) + { + bitbuf_bit(ctx, val & 1); + val >>= 1; + } +} + +void output_bits(struct output_ctx *ctx, /* IN/OUT */ + int count, /* IN */ + int val) /* IN */ +{ + LOG(LOG_DUMP, ("output bits: count = %d, val = %d\n", count, val)); + output_bits_int(ctx, count, val); +} + +void output_gamma_code(struct output_ctx *ctx, /* IN/OUT */ + int code) /* IN */ +{ + LOG(LOG_DUMP, ("output gamma: code = %d\n", code)); + output_bits_int(ctx, 1, 1); + while (code-- > 0) + { + output_bits_int(ctx, 1, 0); + } +} diff --git a/loader/tools/exomizer-3.1/src/output.h b/loader/tools/exomizer-3.1/src/output.h new file mode 100644 index 0000000..74a334c --- /dev/null +++ b/loader/tools/exomizer-3.1/src/output.h @@ -0,0 +1,73 @@ +#ifndef ALREADY_INCLUDED_OUTPUT +#define ALREADY_INCLUDED_OUTPUT +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "buf.h" +#include "flags.h" +#include + +struct output_ctx { + unsigned char bitbuf; + unsigned char bitcount; + int pos; + int start; + struct buf *buf; + int flags_proto; +}; + +void output_ctx_init(struct output_ctx *ctx, /* IN/OUT */ + int flags_proto, /* IN */ + struct buf *out); /* IN/OUT */ + +unsigned int output_get_pos(struct output_ctx *ctx); /* IN */ + +void output_byte(struct output_ctx *ctx, /* IN/OUT */ + unsigned char byte); /* IN */ + +void output_word(struct output_ctx *ctx, /* IN/OUT */ + unsigned short int word); /* IN */ + +void output_bits_flush(struct output_ctx *ctx, /* IN/OUT */ + int add_marker_bit); /* IN */ + +int output_bits_alignment(struct output_ctx *ctx); /* IN */ + +void output_bits(struct output_ctx *ctx, /* IN/OUT */ + int count, /* IN */ + int val); /* IN */ + +void output_gamma_code(struct output_ctx *ctx, /* IN/OUT */ + int code); /* IN */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/parse.c b/loader/tools/exomizer-3.1/src/parse.c new file mode 100644 index 0000000..bc9cbe7 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/parse.c @@ -0,0 +1,1041 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "parse.h" +#include "asm.tab.h" +#include "log.h" +#include "chunkpool.h" +#include "named_buffer.h" +#include "pc.h" +#include "map.h" + +#include + +struct parse { + /* pools */ + struct chunkpool atom_pool; + struct chunkpool vec_pool; + /* one pass data only */ + struct map sym_table; + struct named_buffer named_buffer; + const char *macro_name; + /* history of the multi pass */ + struct map *guesses; + /* initial arguments */ + struct map initial_symbols; + struct named_buffer initial_named_buffer; +}; + +static struct parse s; + +void scanner_init(void); +void scanner_free(void); + +void parse_init(void) +{ + expr_init(); + scanner_init(); + chunkpool_init(&s.atom_pool, sizeof(struct atom)); + chunkpool_init(&s.vec_pool, sizeof(struct vec)); + map_init(&s.sym_table); + named_buffer_init(&s.named_buffer); + + map_init(&s.initial_symbols); + named_buffer_init(&s.initial_named_buffer); +} + +static void parse_reset(void) +{ + map_clear(&s.sym_table); + named_buffer_clear(&s.named_buffer); +} + +static void free_vec_pool(struct vec *v) +{ + vec_free(v, NULL); +} + +void parse_free(void) +{ + named_buffer_free(&s.initial_named_buffer); + map_free(&s.initial_symbols); + + named_buffer_free(&s.named_buffer); + chunkpool_free(&s.atom_pool); + chunkpool_free2(&s.vec_pool, (cb_free*)free_vec_pool); + named_buffer_free(&s.named_buffer); + map_free(&s.sym_table); + scanner_free(); + expr_free(); +} + +int is_valid_i8(i32 value) +{ + return (value >= -128 && value <= 127); +} + +int is_valid_u8(i32 value) +{ + return (value >= 0 && value <= 255); +} + +int is_valid_ui8(i32 value) +{ + return (value >= -128 && value <= 255); +} + +int is_valid_u16(i32 value) +{ + return (value >= 0 && value <= 65535); +} + +int is_valid_ui16(i32 value) +{ + return (value >= -32768 && value <= 65535); +} + +struct expr *new_is_defined(const char *symbol) +{ + int expr_val = (map_get(&s.sym_table, symbol) != NULL); + return new_expr_number(expr_val); +} + +void new_symbol_expr(const char *symbol, struct expr *arg) +{ + if(map_put(&s.sym_table, symbol, arg) != NULL) + { + /* error, symbol redefinition not allowed */ + LOG(LOG_ERROR, ("not allowed to redefine symbol %s\n", symbol)); + exit(1); + } +} + +void new_symbol_expr_guess(const char *symbol, struct expr *arg) +{ + /* Set a soft symbol only if it is not set */ + if(map_get(s.guesses, symbol) == NULL) + { + map_put(s.guesses, symbol, arg); + } +} + +const char *find_symref(const char *symbol, + struct expr **expp) +{ + struct expr *exp; + const char *p; + + exp = NULL; + p = NULL; + if(s.guesses != NULL) + { + exp = map_get(s.guesses, symbol); + } + if(exp == NULL) + { + exp = map_get(&s.sym_table, symbol); + if(exp == NULL) + { + static char buf[1024]; + /* error, symbol not found */ + sprintf(buf, "symbol %s not found", symbol); + p = buf; + LOG(LOG_DEBUG, ("%s\n", p)); + return p; + } + } + + if(expp != NULL) + { + *expp = exp; + } + + return p; +} + +void new_label(const char *label) +{ + struct expr *e = pc_get(); + new_symbol_expr(label, e); +} + +static void dump_sym_table(int level, struct map *m) +{ + struct map_iterator i; + const struct map_entry *e; + + for(map_get_iterator(m, &i); (e = map_iterator_next(&i)) != NULL;) + { + LOG(level, ("sym_table: %s ", e->key)); + expr_dump(level, e->value); + } +} + +void set_initial_symbol(const char *symbol, i32 value) +{ + struct expr *e = new_expr_number(value); + map_put(&s.initial_symbols, symbol, e); +} + +void set_initial_symbol_soft(const char *symbol, i32 value) +{ + if (!map_contains_key(&s.initial_symbols, symbol)) + { + struct expr *e = new_expr_number(value); + map_put(&s.initial_symbols, symbol, e); + } +} + +struct buf *new_initial_named_buffer(const char *name) +{ + return new_named_buffer(&s.initial_named_buffer, name); +} + +static const char *resolve_expr2(struct expr *e, i32 *valp) +{ + struct expr *e2; + i32 value; + i32 value2; + const char *p; + + p = NULL; + LOG(LOG_DEBUG, ("resolve_expr: ")); + + expr_dump(LOG_DEBUG, e); + + switch (e->expr_op) + { + case NUMBER: + /* we are already resolved */ + value = e->type.number; + break; + case vNEG: + p = resolve_expr2(e->type.arg1, &value); + if(p != NULL) break; + value = -value; + break; + case LNOT: + p = resolve_expr2(e->type.arg1, &value); + if(p != NULL) break; + value = !value; + break; + case SYMBOL: + e2 = NULL; + p = find_symref(e->type.symref, &e2); + if(p != NULL) break; + if(e2 == NULL) + { + static char buf[1024]; + /* error, symbol not found */ + sprintf(buf, "symbol %s has no value.", e->type.symref); + p = buf; + LOG(LOG_DEBUG, ("%s\n", p)); + break; + } + p = resolve_expr2(e2, &value); + break; + default: + LOG(LOG_DEBUG, ("binary op %d\n", e->expr_op)); + + p = resolve_expr2(e->type.arg1, &value); + if(p != NULL) break; + + /* short circuit the logical operators */ + if(e->expr_op == LOR) + { + value = (value != 0); + if(value) break; + } + else if(e->expr_op == LAND) + { + value = (value != 0); + if(!value) break; + } + + p = resolve_expr2(e->expr_arg2, &value2); + if(p != NULL) break; + + switch(e->expr_op) + { + case MINUS: + value -= value2; + break; + case PLUS: + value += value2; + break; + case MULT: + value *= value2; + break; + case DIV: + value /= value2; + break; + case MOD: + value %= value2; + break; + case LT: + value = (value < value2); + break; + case GT: + value = (value > value2); + break; + case EQ: + value = (value == value2); + break; + case NEQ: + value = (value != value2); + break; + case LOR: + value = (value2 != 0); + break; + case LAND: + value = (value2 != 0); + break; + default: + LOG(LOG_ERROR, ("unsupported op %d\n", e->expr_op)); + exit(1); + } + } + if(p == NULL) + { + if(e->expr_op != NUMBER) + { + /* shortcut future recursion */ + e->expr_op = NUMBER; + e->type.number = value; + } + if(valp != NULL) + { + *valp = value; + } + } + + return p; +} + +static i32 resolve_expr(struct expr *e) +{ + i32 val; + const char *p; + + p = resolve_expr2(e, &val); + if(p != NULL) + { + LOG(LOG_ERROR, ("%s\n", p)); + exit(1); + } + return val; +} + +struct expr *new_expr_inclen(const char *name) +{ + long length; + struct buf *in; + struct expr *expr; + + in = get_named_buffer(&s.named_buffer, name); + length = buf_size(in); + + expr = new_expr_number((i32)length); + return expr; +} + +struct expr *new_expr_incword(const char *name, + struct expr *skip) +{ + i32 word; + i32 offset; + long length; + struct buf *in; + struct expr *expr; + unsigned char *p; + + offset = resolve_expr(skip); + in = get_named_buffer(&s.named_buffer, name); + length = buf_size(in); + if(offset < 0) + { + offset += length; + } + if(offset < 0 || offset > length - 2) + { + LOG(LOG_ERROR, + ("Can't read word from offset %d in file \"%s\".\n", + offset, name)); + exit(1); + } + p = buf_data(in); + p += offset; + word = *p++; + word |= *p++ << 8; + + expr = new_expr_number(word); + return expr; +} + +void set_org(struct expr *arg) +{ + /* org assembler directive */ + pc_set_expr(arg); + LOG(LOG_DEBUG, ("setting .org to ???\n")); + return; +} + +void push_macro_state(const char *name) +{ + s.macro_name = name; + push_state_macro = 1; + new_named_buffer(&s.named_buffer, name); +} + +void macro_append(const char *text) +{ + struct buf *mb; + + LOG(LOG_DEBUG, ("appending >>%s<< to macro\n", text)); + + mb = get_named_buffer(&s.named_buffer, s.macro_name); + buf_append(mb, text, strlen(text)); +} + +void push_if_state(struct expr *arg) +{ + int val; + LOG(LOG_DEBUG, ("resolving if expression\n")); + val = resolve_expr(arg); + LOG(LOG_DEBUG, ("if expr resolved to %d\n", val)); + if(val) + { + push_state_init = 1; + } + else + { + push_state_skip = 1; + } +} + +struct atom *new_op(u8 op_code, u8 atom_op_type, + struct expr *op_arg) +{ + struct atom *atom; + + atom = chunkpool_malloc(&s.atom_pool); + atom->type = atom_op_type; + atom->u.op.code = op_code; + atom->u.op.arg = op_arg; + + switch(atom_op_type) + { + case ATOM_TYPE_OP_ARG_NONE: + pc_add(1); + break; + case ATOM_TYPE_OP_ARG_U8: + pc_add(2); + break; + case ATOM_TYPE_OP_ARG_U16: + pc_add(3); + break; + case ATOM_TYPE_OP_ARG_I8: + pc_add(2); + atom->u.op.arg = new_expr_op2(MINUS, atom->u.op.arg, pc_get()); + break; + case ATOM_TYPE_OP_ARG_UI8: + pc_add(2); + break; + default: + LOG(LOG_ERROR, ("invalid op arg range %d\n", atom_op_type)); + exit(1); + } + pc_dump(LOG_DEBUG); + + return atom; +} + +struct atom *new_op0(u8 op_code) +{ + struct atom *atom; + atom = new_op(op_code, ATOM_TYPE_OP_ARG_NONE, NULL); + return atom; +} + +struct atom *new_exprs(struct expr *arg) +{ + struct atom *atom; + + atom = chunkpool_malloc(&s.atom_pool); + atom->type = ATOM_TYPE_EXPRS; + atom->u.exprs = chunkpool_malloc(&s.vec_pool); + vec_init(atom->u.exprs, sizeof(struct expr*)); + exprs_add(atom, arg); + return atom; +} + +struct atom *exprs_add(struct atom *atom, struct expr *arg) +{ + if(atom->type != ATOM_TYPE_EXPRS) + { + LOG(LOG_ERROR, ("can't add expr to atom of type %d\n", atom->type)); + exit(1); + } + vec_push(atom->u.exprs, &arg); + return atom; +} + +struct atom *exprs_to_byte_exprs(struct atom *atom) +{ + if(atom->type != ATOM_TYPE_EXPRS) + { + LOG(LOG_ERROR, ("can't convert atom of type %d to byte exprs.\n", + atom->type)); + exit(1); + } + atom->type = ATOM_TYPE_BYTE_EXPRS; + + pc_add(vec_size(atom->u.exprs)); + return atom; +} + +struct atom *exprs_to_word_exprs(struct atom *atom) +{ + if(atom->type != ATOM_TYPE_EXPRS) + { + LOG(LOG_ERROR, ("can't convert exprs of type %d to word exprs.\n", + atom->type)); + exit(1); + } + atom->type = ATOM_TYPE_WORD_EXPRS; + + pc_add(vec_size(atom->u.exprs) * 2); + return atom; +} + +struct atom *new_res(struct expr *len, struct expr *value) +{ + struct atom *atom; + + atom = chunkpool_malloc(&s.atom_pool); + atom->type = ATOM_TYPE_RES; + atom->u.res.length = len; + atom->u.res.value = value; + + pc_add_expr(len); + return atom; +} + +struct atom *new_incbin(const char *name, + struct expr *skip, struct expr *len) +{ + struct atom *atom; + long length; + i32 len32; + i32 skip32; + struct buf *in; + + /* find out how long the file is */ + in = get_named_buffer(&s.named_buffer, name); + length = buf_size(in); + + skip32 = 0; + if(skip != NULL) + { + skip32 = resolve_expr(skip); + } + if(skip32 < 0) + { + skip32 += length; + } + if(skip32 < 0 || skip32 > length) + { + LOG(LOG_ERROR, + ("Can't read from offset %d in file \"%s\".\n", skip32, name)); + exit(1); + } + length -= skip32; + + len32 = 0; + if(len != NULL) + { + len32 = resolve_expr(len); + } + if(len32 < 0) + { + len32 += length; + } + if(len32 < 0 || len32 > length) + { + LOG(LOG_ERROR, + ("Can't read %d bytes from offset %d from file \"%s\".\n", + len32, skip32, name)); + exit(1); + } + + atom = chunkpool_malloc(&s.atom_pool); + atom->type = ATOM_TYPE_BUFFER; + atom->u.buffer.name = name; + atom->u.buffer.length = len32; + atom->u.buffer.skip = skip32; + + if(len != NULL) + { + pc_add(len32); + } + return atom; +} + + +void asm_error(const char *msg) +{ + LOG(LOG_ERROR, ("Error: %s\n", msg)); + exit(1); +} + +void asm_echo(const char *msg, struct atom *atom) +{ + struct vec_iterator i; + struct expr **exprp; + int count = 0; + i32 e[10]; + + if(atom != NULL) + { + if(atom->type != ATOM_TYPE_EXPRS || vec_size(atom->u.exprs) > 10) + { + LOG(LOG_ERROR, ("echo arguments must be a string followed by none " + "or at most ten expressions.\n")); + exit(1); + } + + vec_get_iterator(atom->u.exprs, &i); + while((exprp = vec_iterator_next(&i)) != NULL) + { + e[count++] = resolve_expr(*exprp); + } + } + for(; count < 10; ++count) + { + e[count] = 0; + } + fprintf(stdout, msg, e[0], e[1], e[2], e[3], + e[4], e[5], e[6], e[7], e[8], e[9]); + fprintf(stdout, "\n"); +} + +void asm_include(const char *msg) +{ + struct buf *src; + + src = get_named_buffer(&s.named_buffer, msg); + asm_src_buffer_push(src); +} + +void initial_symbol_dump(int level, const char *symbol) +{ + i32 value; + struct expr *expr; + + expr = map_get(&s.initial_symbols, symbol); + if(expr != NULL) + { + value = resolve_expr(expr); + LOG(level, ("symbol \"%s\" resolves to %d ($%04X)\n", + symbol, value, value)); + } + else + { + if(map_contains_key(&s.initial_symbols, symbol)) + { + LOG(level, ("symbol \"%s\" defined but has no value\n", symbol)); + } + else + { + LOG(level, ("symbol \"%s\" not found\n", symbol)); + } + } +} + +int resolve_symbol(const char *symbol, int *has_valuep, i32 *valuep) +{ + int found = 0; + int has_value = 0; + i32 value = 0; + struct expr *e = NULL; + const char *p; + + p = find_symref(symbol, &e); + if(p == NULL) + { + if(e != NULL) + { + value = resolve_expr(e); + has_value = 1; + } + found = 1; + } + if(found) + { + if(has_valuep != NULL) + { + *has_valuep = has_value; + } + if(has_value && valuep != NULL) + { + *valuep = value; + } + } + return found; +} + +void symbol_dump_resolved(int level, const char *symbol) +{ + int has_value; + i32 value; + + if(resolve_symbol(symbol, &has_value, &value)) + { + if(has_value) + { + LOG(level, ("symbol \"%s\" resolves to %d ($%04X)\n", + symbol, value, value)); + } + else + { + LOG(level, ("symbol \"%s\" is defined but has no value\n", + symbol)); + } + } + else + { + LOG(level, ("symbol \"%s\" not found\n", symbol)); + } +} + +void output_atoms(struct buf *out, struct vec *atoms) +{ + struct vec_iterator i; + struct vec_iterator i2; + struct atom **atomp; + struct atom *atom; + struct expr **exprp; + struct expr *expr; + struct buf *in; + const char *p; + i32 value; + i32 value2; + + dump_sym_table(LOG_DEBUG, &s.sym_table); + + vec_get_iterator(atoms, &i); + while((atomp = vec_iterator_next(&i)) != NULL) + { + atom = *atomp; + + LOG(LOG_DEBUG, ("yadda\n")); + + switch(atom->type) + { + case ATOM_TYPE_OP_ARG_NONE: + LOG(LOG_DEBUG, ("output: $%02X\n", atom->u.op.code)); + buf_append_char(out, atom->u.op.code); + break; + case ATOM_TYPE_OP_ARG_U8: + /* op with argument */ + value = resolve_expr(atom->u.op.arg); + if(!is_valid_u8(value)) + { + LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", + value, atom->u.op.code, (void*)atom)); + exit(1); + } + LOG(LOG_DEBUG, ("output: $%02X $%02X\n", + atom->u.op.code, value & 255)); + buf_append_char(out, atom->u.op.code); + buf_append_char(out, value); + break; + case ATOM_TYPE_OP_ARG_I8: + /* op with argument */ + value = resolve_expr(atom->u.op.arg); + if(!is_valid_i8(value)) + { + LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", + value, atom->u.op.code, (void*)atom)); + exit(1); + } + LOG(LOG_DEBUG, ("output: $%02X $%02X\n", + atom->u.op.code, value & 255)); + buf_append_char(out, atom->u.op.code); + buf_append_char(out, value); + break; + case ATOM_TYPE_OP_ARG_UI8: + /* op with argument */ + value = resolve_expr(atom->u.op.arg); + if(!is_valid_ui8(value)) + { + LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", + value, atom->u.op.code, (void*)atom)); + exit(1); + } + LOG(LOG_DEBUG, ("output: $%02X $%02X\n", + atom->u.op.code, value & 255)); + buf_append_char(out, atom->u.op.code); + buf_append_char(out, value); + break; + case ATOM_TYPE_OP_ARG_U16: + /* op with argument */ + value = resolve_expr(atom->u.op.arg); + if(!is_valid_u16(value)) + { + LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", + value, atom->u.op.code, (void*)atom)); + exit(1); + } + value2 = value / 256; + value = value % 256; + LOG(LOG_DEBUG, ("output: $%02X $%02X $%02X\n", + atom->u.op.code, + value, value2)); + buf_append_char(out, atom->u.op.code); + buf_append_char(out, value); + buf_append_char(out, value2); + break; + case ATOM_TYPE_RES: + /* reserve memory statement */ + value = resolve_expr(atom->u.res.length); + if(!is_valid_u16(value)) + { + LOG(LOG_ERROR, ("length %d for .res(length, value) " + "is out of range\n", value)); + exit(1); + } + value2 = resolve_expr(atom->u.res.value); + if(!is_valid_ui8(value2)) + { + LOG(LOG_ERROR, ("value %d for .res(length, value) " + "is out of range\n", value)); + exit(1); + } + LOG(LOG_DEBUG, ("output: .RES %d, %d\n", value, value2)); + while(--value >= 0) + { + buf_append_char(out, value2); + } + break; + case ATOM_TYPE_BUFFER: + /* include binary file statement */ + value = atom->u.buffer.skip; + if(!is_valid_u16(value)) + { + LOG(LOG_ERROR, ("value %d for .res(length, value) " + "is out of range\n", value)); + exit(1); + } + value2 = atom->u.buffer.length; + if(!is_valid_u16(value2)) + { + LOG(LOG_ERROR, ("length %d for .incbin(name, skip, length) " + "is out of range\n", value2)); + exit(1); + } + LOG(LOG_DEBUG, ("output: .INCBIN \"%s\", %d, %d\n", + atom->u.buffer.name, value, value2)); + in = get_named_buffer(&s.named_buffer, atom->u.buffer.name); + p = buf_data(in); + p += value; + while(--value2 >= 0) + { + buf_append_char(out, *p++); + } + break; + case ATOM_TYPE_WORD_EXPRS: + vec_get_iterator(atom->u.exprs, &i2); + while((exprp = vec_iterator_next(&i2)) != NULL) + { + expr = *exprp; + value = resolve_expr(expr); + if(!is_valid_ui16(value)) + { + LOG(LOG_ERROR, ("value %d for .word(value, ...) " + "is out of range\n", value)); + } + value2 = value / 256; + value = value % 256; + buf_append_char(out, value); + buf_append_char(out, value2); + } + LOG(LOG_DEBUG, ("output: %d words\n", vec_size(atom->u.exprs))); + break; + case ATOM_TYPE_BYTE_EXPRS: + vec_get_iterator(atom->u.exprs, &i2); + while((exprp = vec_iterator_next(&i2)) != NULL) + { + expr = *exprp; + value = resolve_expr(expr); + if(!is_valid_ui8(value)) + { + LOG(LOG_ERROR, ("value %d for .byte(value, ...) " + "is out of range\n", value)); + } + buf_append_char(out, value); + } + LOG(LOG_DEBUG, ("output: %d bytes\n", vec_size(atom->u.exprs))); + break; + default: + LOG(LOG_ERROR, ("invalid atom_type %d @%p\n", + atom->type, (void*)atom)); + exit(1); + } + } +} + +static int expr_cmp_cb(const void *a, const void *b) +{ + int result = 0; + i32 e1v = resolve_expr((struct expr*)a); + i32 e2v = resolve_expr((struct expr*)b); + + if(e1v < e2v) + { + result = -1; + } + else if(e1v > e2v) + { + result = 1; + } + return result; +} + +static int loopDetect(struct vec *guesses_history) +{ + int result = 0; + struct vec_iterator i; + struct map *m; + for(vec_get_iterator(guesses_history, &i); + (m = vec_iterator_next(&i)) != NULL;) + { + if(map_equals(m, s.guesses, expr_cmp_cb)) + { + result = 1; + break; + } + } + return result; +} + +static int wasFinalPass(void) +{ + int result = 1; + struct map_iterator i; + struct map_entry *me; + for(map_get_iterator(s.guesses, &i); + (me = (struct map_entry*)map_iterator_next(&i)) != NULL;) + { + struct expr *guess_expr; + struct expr *sym_expr; + + LOG(LOG_VERBOSE, ("Checking guessed symbol %s: ", me->key)); + /* Was this guessed symbol used in this pass? */ + if((sym_expr = map_get(&s.sym_table, me->key)) == NULL) + { + /* No, skip it */ + continue; + } + guess_expr = me->value; + LOG(LOG_VERBOSE, ("expected %d, actual %d\n", + resolve_expr(guess_expr), + resolve_expr(sym_expr))); + + if(expr_cmp_cb(me->value, sym_expr) != 0) + { + /* Not the same, not the last pass. + * copy the actual value from the sym table */ + me->value = sym_expr; + result = 0; + } + } + return result; +} + +int assemble(struct buf *source, struct buf *dest) +{ + struct vec guesses_history; + struct map guesses_storage; + int dest_pos; + int result; + + dump_sym_table(LOG_DEBUG, &s.initial_symbols); + + vec_init(&guesses_history, sizeof(struct map)); + s.guesses = NULL; + dest_pos = buf_size(dest); + for(;;) + { + + map_put_all(&s.sym_table, &s.initial_symbols); + named_buffer_copy(&s.named_buffer, &s.initial_named_buffer); + map_init(&guesses_storage); + + if(s.guesses != NULL) + { + /* copy updated guesses from latest pass */ + map_put_all(&guesses_storage, s.guesses); + } + s.guesses = &guesses_storage; + + result = assembleSinglePass(source, dest); + if(result != 0) + { + /* the assemble pass failed */ + break; + } + + /* check if any guessed symbols was wrong and update them + * to their actual value */ + if(wasFinalPass()) + { + /* The assemble pass succeeded without any wrong guesses, + * we're done */ + break; + } + if(loopDetect(&guesses_history)) + { + /* More passes would only get us into a loop */ + LOG(LOG_VERBOSE, ("Aborting due to loop.\n")); + result = -1; + break; + } + + LOG(LOG_VERBOSE, ("Trying another pass.\n")); + + /* allocate storage for the guesses in the history vector */ + s.guesses = vec_push(&guesses_history, s.guesses); + + parse_reset(); + buf_remove(dest, dest_pos, -1); + } + map_free(&guesses_storage); + vec_free(&guesses_history, (cb_free*)map_free); + s.guesses = NULL; + return result; +} diff --git a/loader/tools/exomizer-3.1/src/parse.h b/loader/tools/exomizer-3.1/src/parse.h new file mode 100644 index 0000000..2261ba4 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/parse.h @@ -0,0 +1,147 @@ +#ifndef INCLUDED_PARSE +#define INCLUDED_PARSE +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "int.h" +#include "vec.h" +#include "buf.h" +#include "expr.h" +#include "map.h" +#include "named_buffer.h" +#include "chunkpool.h" +#include "pc.h" + +#define ATOM_TYPE_OP_ARG_NONE 0 /* uses u.op */ +#define ATOM_TYPE_OP_ARG_U8 1 /* uses u.op */ +#define ATOM_TYPE_OP_ARG_U16 2 /* uses u.op */ +#define ATOM_TYPE_OP_ARG_I8 3 /* uses u.op */ +#define ATOM_TYPE_OP_ARG_UI8 4 /* uses u.op */ +#define ATOM_TYPE_EXPRS 12 /* uses u.exprs */ +#define ATOM_TYPE_WORD_EXPRS 10 /* uses u.exprs */ +#define ATOM_TYPE_BYTE_EXPRS 11 /* uses u.exprs */ +#define ATOM_TYPE_RES 13 /* uses u.res */ +#define ATOM_TYPE_BUFFER 14 /* uses u.buffer */ + +struct op +{ + struct expr *arg; + u8 code; +}; + +struct res +{ + struct expr *length; + struct expr *value; +}; + +struct buffer +{ + const char *name; + i32 length; + i32 skip; +}; + +struct atom +{ + u8 type; + union + { + struct op op; + struct vec *exprs; + struct buffer buffer; + struct res res; + } u; +}; + +extern int push_state_skip; +extern int push_state_macro; +extern int push_state_init; +extern int num_lines; + +void parse_init(void); +void parse_free(void); + +void set_initial_symbol(const char *symbol, i32 value); +void set_initial_symbol_soft(const char *symbol, i32 value); +void initial_symbol_dump(int level, const char *symbol); + +struct buf *new_initial_named_buffer(const char *name); + +int assemble(struct buf *source, struct buf *dest); + +/* start of internal functions */ + +struct atom *new_op(u8 op_code, u8 op_size, struct expr *arg); +struct atom *new_op0(u8 op_code); + +struct atom *new_exprs(struct expr *arg); +struct atom *exprs_add(struct atom *atom, struct expr *arg); +struct atom *exprs_to_byte_exprs(struct atom *atom); +struct atom *exprs_to_word_exprs(struct atom *atom); + +struct atom *new_res(struct expr *len, struct expr *value); +struct atom *new_incbin(const char *name, + struct expr *skip, struct expr *len); + +struct expr *new_is_defined(const char *symbol); +struct expr *new_expr_inclen(const char *name); +struct expr *new_expr_incword(const char *name, + struct expr *skip); + +void new_symbol_expr(const char *symbol, struct expr *arg); +void new_symbol_expr_guess(const char *symbol, struct expr *arg); + +/* returns NULL if found, not otherwise, expp may be NULL. */ +const char *find_symref(const char *symbol, + struct expr **expp); + +int resolve_symbol(const char *symbol, int *has_valuep, i32 *valuep); +void symbol_dump_resolved(int level, const char *symbol); + +void new_label(const char *label); +void set_org(struct expr *arg); +void push_if_state(struct expr *arg); +void push_macro_state(const char *name); +void macro_append(const char *text); +void asm_error(const char *msg); +void asm_echo(const char *msg, struct atom *atom); +void asm_include(const char *msg); + +void output_atoms(struct buf *out, struct vec *mem); +void asm_src_buffer_push(struct buf *buf); + +int assembleSinglePass(struct buf *source, struct buf *dest); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/pc.c b/loader/tools/exomizer-3.1/src/pc.c new file mode 100644 index 0000000..b13e7ae --- /dev/null +++ b/loader/tools/exomizer-3.1/src/pc.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "pc.h" +#include "log.h" +#include + +struct pc { + struct expr *pc1; + int pc2; +}; + +static struct expr unset_value; +static struct pc p = {&unset_value, 0}; + +void pc_dump(int level) +{ +} + +void pc_set(int pc) +{ + p.pc1 = NULL; + p.pc2 = pc; +} + +void pc_set_expr(struct expr *pc) +{ + p.pc1 = pc; + p.pc2 = 0; +} + +struct expr *pc_get(void) +{ + struct expr *old_pc1; + + if(p.pc1 == &unset_value) + { + LOG(LOG_ERROR, ("PC must be set by a .org(pc) call.\n")); + exit(1); + } + if(p.pc1 == NULL || p.pc2 != 0) + { + old_pc1 = p.pc1; + p.pc1 = new_expr_number(p.pc2); + p.pc2 = 0; + if(old_pc1 != NULL) + { + p.pc1 = new_expr_op2(PLUS, p.pc1, old_pc1); + } + } + + return p.pc1; +} + +void pc_add(int offset) +{ + if(p.pc1 != &unset_value) + { + p.pc2 += offset; + } +} + +void pc_add_expr(struct expr *pc) +{ + struct expr *old_pc1; + + if(p.pc1 != &unset_value) + { + old_pc1 = p.pc1; + p.pc1 = pc; + if(old_pc1 != NULL) + { + p.pc1 = new_expr_op2(PLUS, p.pc1, old_pc1); + } + } +} + +void pc_unset(void) +{ + pc_set_expr(&unset_value); +} diff --git a/loader/tools/exomizer-3.1/src/pc.h b/loader/tools/exomizer-3.1/src/pc.h new file mode 100644 index 0000000..8430410 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/pc.h @@ -0,0 +1,47 @@ +#ifndef ALREADY_INCLUDED_PC +#define ALREADY_INCLUDED_PC +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "expr.h" + +void pc_dump(int level); +void pc_set(int pc); +void pc_set_expr(struct expr *pc); +struct expr *pc_get(void); +void pc_add(int offset); +void pc_add_expr(struct expr *pc); +void pc_unset(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/perf.c b/loader/tools/exomizer-3.1/src/perf.c new file mode 100644 index 0000000..a8806e9 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/perf.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "perf.h" +#include "table.h" + +static int size_cycles_asc_cmp(const void *a, const void *b) +{ + const struct measurement *p1 = a; + const struct measurement *p2 = b; + int i = p1->size - p2->size; + if (i == 0) + { + i = p1->cycles - p2->cycles; + } + return i; +} + +static int cycles_size_asc_cmp(const void *a, const void *b) +{ + const struct measurement *p1 = a; + const struct measurement *p2 = b; + int i = p1->cycles - p2->cycles; + if (i == 0) + { + i = p1->size - p2->size; + } + return i; +} + +void perf_init(struct perf_ctx *perf) +{ + vec_init(&perf->measurements, sizeof (struct measurement)); +} +void perf_free(struct perf_ctx *perf) +{ + vec_free(&perf->measurements, NULL); +} + +void perf_add(struct perf_ctx *perf, const char *name, + int in_size, int out_size, int cycles) +{ + float reduced = 100.0 * (out_size - in_size) / out_size; + float cb_out = (float)cycles / out_size; + float bkc_out = 1000 * out_size / (float)cycles; + struct measurement *m = vec_push(&perf->measurements, NULL); + m->name = name; + m->size = in_size; + m->reduced = reduced; + m->cycles = cycles; + m->cb_out = cb_out; + m->bkc_out = bkc_out; + m->hidden = 0; +} + +void perf_sort_size_cycles(struct perf_ctx *perf, int pareto_hide) +{ + struct vec_iterator i; + struct measurement *m; + int cycles = -1; + vec_sort(&perf->measurements, size_cycles_asc_cmp); + vec_get_iterator(&perf->measurements, &i); + while ((m = vec_iterator_next(&i)) != NULL) + { + if (cycles == -1 || cycles > m->cycles) + { + cycles = m->cycles; + m->hidden = 0; + } + else if (pareto_hide) + { + m->hidden = 1; + } + } +} + +void perf_sort_cycles_size(struct perf_ctx *perf, int pareto_hide) +{ + struct vec_iterator i; + struct measurement *m; + int size = -1; + vec_sort(&perf->measurements, cycles_size_asc_cmp); + vec_get_iterator(&perf->measurements, &i); + while ((m = vec_iterator_next(&i)) != NULL) + { + if (size == -1 || size > m->size) + { + size = m->size; + m->hidden = 0; + } + else if (pareto_hide) + { + m->hidden = 1; + } + } +} + +void perf_buf_print(struct perf_ctx *perf, struct buf *target) +{ + struct table_ctx table; + struct vec_iterator i; + struct measurement *m; + + table_init(&table, 1); + table_col_add(&table, "File name", "%s", 0); + table_col_add(&table, "Size", "%d", 1); + table_col_add(&table, "Reduced", "%.2f%%", 1); + table_col_add(&table, "Cycles", "%d", 1); + table_col_add(&table, "C/B", "%.2f", 1); + table_col_add(&table, "B/kC", "%.2f", 1); + + vec_get_iterator(&perf->measurements, &i); + while ((m = vec_iterator_next(&i)) != NULL) + { + if (!m->hidden) + { + table_row_add(&table, m->name, m->size, m->reduced, + m->cycles, m->cb_out, m->bkc_out); + } + } + + table_buf_print(&table, target); + + table_free(&table); +} + +void perf_fprint(FILE *f, struct perf_ctx *perf) +{ + struct buf buf = STATIC_BUF_INIT; + perf_buf_print(perf, &buf); + fprintf(f, "%s", (char*)buf_data(&buf)); + buf_free(&buf); +} diff --git a/loader/tools/exomizer-3.1/src/perf.h b/loader/tools/exomizer-3.1/src/perf.h new file mode 100644 index 0000000..1136c66 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/perf.h @@ -0,0 +1,72 @@ +#ifndef ALREADY_INCLUDED_PERF +#define ALREADY_INCLUDED_PERF +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2020 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "callback.h" +#include "vec.h" +#include "buf.h" + +struct measurement +{ + const char *name; + int size; + float reduced; + int cycles; + float cb_out; + float bkc_out; + int hidden; +}; + +#define STATIC_PERF_INIT {STATIC_VEC_INIT(sizeof(struct measurement))} + +struct perf_ctx +{ + struct vec measurements; +}; + +void perf_init(struct perf_ctx *perf); +void perf_free(struct perf_ctx *perf); + +void perf_add(struct perf_ctx *perf, const char *name, + int in_size, int out_size, int cycles); + +void perf_sort_size_cycles(struct perf_ctx *perf, int pareto_hide); +void perf_sort_cycles_size(struct perf_ctx *perf, int pareto_hide); + +void perf_buf_print(struct perf_ctx *perf, struct buf *target); + +void perf_fprint(FILE *f, struct perf_ctx *perf); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/progress.c b/loader/tools/exomizer-3.1/src/progress.c new file mode 100644 index 0000000..354eb74 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/progress.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "progress.h" +#include "log.h" +#include + +#define BAR_LENGTH 64 + +void progress_init(struct progress *p, char *msg, int start, int end) +{ + if(start > end) + { + p->factor = (float)BAR_LENGTH / (end - start); + p->offset = -start; + } + else + { + p->factor = (float)BAR_LENGTH / (start - end); + p->offset = start; + } + p->last = -1; + if(msg == NULL) + { + msg = "progressing_"; + } + p->msg = msg; + + LOG(LOG_DEBUG, ("start %d, end %d, pfactor %f, poffset %d\n", + start, end, p->factor, p->offset)); +} + +void progress_bump(struct progress *p, int pos) +{ + int fraction = ((pos + p->offset) * p->factor) + 0.5; + while(fraction > p->last) + { + if(p->last == -1) + { + LOG_TTY(LOG_NORMAL, (" %*s]\r [", BAR_LENGTH, "")); + } + else + { + LOG_TTY(LOG_NORMAL, ("%c", p->msg[p->last % strlen(p->msg)])); + } + p->last += 1; + } +} + +void progress_free(struct progress *p) +{ + LOG_TTY(LOG_NORMAL, ("\n")); +} diff --git a/loader/tools/exomizer-3.1/src/progress.h b/loader/tools/exomizer-3.1/src/progress.h new file mode 100644 index 0000000..817ce15 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/progress.h @@ -0,0 +1,51 @@ +#ifndef ALREADY_INCLUDED_PROGRESS +#define ALREADY_INCLUDED_PROGRESS +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +struct progress +{ + char *msg; + float factor; + int offset; + int last; +}; + +void progress_init(struct progress *p, char *msg, int start, int end); + +void progress_bump(struct progress *p, int pos); + +void progress_free(struct progress *p); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/radix.c b/loader/tools/exomizer-3.1/src/radix.c new file mode 100644 index 0000000..0c67f4a --- /dev/null +++ b/loader/tools/exomizer-3.1/src/radix.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "radix.h" +#include "chunkpool.h" + +#define RADIX_TREE_NODE_RADIX 8U +#define RADIX_TREE_NODE_MASK ((1U << RADIX_TREE_NODE_RADIX) - 1U) + +struct radix_node { + struct radix_node *rn; +}; + +void radix_tree_init(struct radix_root *rr) /* IN */ +{ + rr->depth = 0; + rr->root = NULL; + + chunkpool_init(&rr->mem, (1 << RADIX_TREE_NODE_RADIX) * sizeof(void *)); +} + +static +void radix_tree_free_helper(int depth, struct radix_node *rnp, + free_callback * f, /* IN */ + void *priv) /* IN */ +{ + int i; + do + { + if (depth == 0) + { + /* do something to the data pointer? */ + if (f != NULL) + { + f(rnp, priv); + } + break; + } + if (rnp == NULL) + { + /* tree not grown here */ + break; + } + + for (i = RADIX_TREE_NODE_MASK; i >= 0; --i) + { + radix_tree_free_helper(depth - 1, rnp[i].rn, f, priv); + rnp[i].rn = NULL; + } + } + while (0); +} + +void radix_tree_free(struct radix_root *rr, /* IN */ + free_callback * f, /* IN */ + void *priv) /* IN */ +{ + radix_tree_free_helper(rr->depth, rr->root, f, priv); + rr->depth = 0; + rr->root = NULL; + chunkpool_free(&rr->mem); +} + +void radix_node_set(struct radix_root *rrp, /* IN */ + unsigned int index, /* IN */ + void *data) /* IN */ +{ + struct radix_node *rnp; + struct radix_node **rnpp; + unsigned int mask; + int depth; + + mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth); + while (index & mask) + { + /*LOG(LOG_DUMP, ("calloc called\n")); */ + /* not deep enough, let's deepen the tree */ + rnp = chunkpool_calloc(&rrp->mem); + + rnp[0].rn = rrp->root; + rrp->root = rnp; + rrp->depth += 1; + + mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth); + } + + /* go down */ + rnpp = &rrp->root; + for (depth = rrp->depth - 1; depth >= 0; --depth) + { + unsigned int node_index; + + if (*rnpp == NULL) + { + /*LOG(LOG_DUMP, ("calloc called\n")); */ + /* tree is not grown in this interval */ + *rnpp = chunkpool_calloc(&rrp->mem); + } + node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) & + RADIX_TREE_NODE_MASK); + + rnpp = &((*rnpp)[node_index].rn); + } + *rnpp = data; +} + +void *radix_node_get(struct radix_root *rr, /* IN */ + unsigned int index) /* IN */ +{ + struct radix_node *rnp; + unsigned short int depth; + + /* go down */ + rnp = rr->root; + for (depth = rr->depth - 1; depth < 0xffff; --depth) + { + unsigned short int node_index; + + if (rnp == NULL) + { + /* tree is not grown in this interval */ + break; + } + node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) & + RADIX_TREE_NODE_MASK); + + rnp = rnp[node_index].rn; + } + return rnp; +} diff --git a/loader/tools/exomizer-3.1/src/radix.h b/loader/tools/exomizer-3.1/src/radix.h new file mode 100644 index 0000000..25fc5a9 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/radix.h @@ -0,0 +1,61 @@ +#ifndef ALREADY_INCLUDED_RADIX +#define ALREADY_INCLUDED_RADIX +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "chunkpool.h" + +struct radix_root { + int depth; + struct radix_node *root; + struct chunkpool mem; +}; + +typedef void free_callback(void *data, void *priv); + +/* *f will be called even for null pointers */ +void radix_tree_free(struct radix_root *rr, /* IN */ + free_callback * f, /* IN */ + void *priv); /* IN */ + +void radix_tree_init(struct radix_root *rr); /* IN */ + +void radix_node_set(struct radix_root *rr, /* IN */ + unsigned int index, /* IN */ + void *data); /* IN */ + +void *radix_node_get(struct radix_root *rr, /* IN */ + unsigned int index); /* IN */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/search.c b/loader/tools/exomizer-3.1/src/search.c new file mode 100644 index 0000000..f23e20f --- /dev/null +++ b/loader/tools/exomizer-3.1/src/search.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2002 - 2018 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "search.h" +#include "progress.h" + +static void update_snp(struct search_node *snp, + float total_score, + unsigned int total_offset, + struct search_node *prev, + struct match *match, + int flags_proto) +{ + unsigned int latest_offset = 0; + snp->total_score = total_score; + snp->total_offset = total_offset; + snp->prev = prev; + snp->match = *match; + + if ((flags_proto & PFLAG_REUSE_OFFSET) != 0) + { + if (match->offset == 0) + { + /* we are a literal */ + struct match *prev_match = &prev->match; + if (prev_match->offset > 0) + { + /* and the previous was a sequence, set the offset */ + latest_offset = prev_match->offset; + } + } + } + snp->latest_offset = latest_offset; +} + +void search_buffer(struct match_ctx *ctx, /* IN */ + encode_match_f * f, /* IN */ + struct encode_match_data *emd, /* IN */ + int flags_proto, /* IN */ + int flags_notrait, /* IN */ + int max_sequence_length, /* IN */ + int greedy, /* IN */ + struct search_node **result)/* OUT */ +{ + struct progress prog; + struct search_node *sn_arr; + const struct match *mp = NULL; + struct search_node *snp; + struct search_node *best_copy_snp; + int best_copy_len; + + struct search_node *best_rle_snp; + + int use_literal_sequences = !(flags_notrait & TFLAG_LIT_SEQ); + int skip_len0123_mirrors = flags_notrait & TFLAG_LEN0123_SEQ_MIRRORS; + int len = ctx->len + 1; + + if (skip_len0123_mirrors) + { + if (flags_proto & PFLAG_4_OFFSET_TABLES) + { + skip_len0123_mirrors = 4; + } + else + { + skip_len0123_mirrors = 3; + } + } + + progress_init(&prog, "finding.shortest.path.",len, 0); + + sn_arr = malloc(len * sizeof(struct search_node)); + memset(sn_arr, 0, len * sizeof(struct search_node)); + + --len; + snp = &sn_arr[len]; + snp->index = len; + snp->match.offset = 0; + snp->match.len = 0; + snp->total_offset = 0; + snp->total_score = 0; + snp->prev = NULL; + snp->latest_offset = 0; + + best_copy_snp = snp; + best_copy_len = 0; + + best_rle_snp = NULL; + + /* think twice about changing this code, + * it works the way it is. The last time + * I examined this code I was certain it was + * broken and broke it myself, trying to fix it. */ + for (;;) + { + float prev_score; + float latest_offset_sum; + + if (use_literal_sequences) + { + /* check if we can do even better with copy */ + snp = &sn_arr[len]; + if((snp->match.offset != 0 || snp->match.len != 1) && + (best_copy_snp->total_score+best_copy_len * 8.0 - + snp->total_score > 0.0 || + best_copy_len > max_sequence_length)) + { + /* found a better copy endpoint */ + LOG(LOG_DEBUG, + ("best copy start moved to index %d\n", snp->index)); + best_copy_snp = snp; + best_copy_len = 0; + } else + { + float copy_score = best_copy_len * 8.0 + (1.0 + 17.0 + 17.0); + float total_copy_score = best_copy_snp->total_score + + copy_score; + + LOG(LOG_DEBUG, + ("total score %0.1f, copy total score %0.1f\n", + snp->total_score, total_copy_score)); + + if (snp->total_score > total_copy_score && + best_copy_len <= max_sequence_length && + !(skip_len0123_mirrors && + /* must be < 2 due to PBIT_IMPL_1LITERAL adjustment */ + best_copy_len > 255 && (best_copy_len & 255) < 2)) + { + struct match local_m; + /* here it is good to just copy instead of crunch */ + + LOG(LOG_DEBUG, + ("copy index %d, len %d, total %0.1f, copy %0.1f\n", + snp->index, best_copy_len, + snp->total_score, total_copy_score)); + + local_m.len = best_copy_len; + local_m.offset = 0; + local_m.next = NULL; + + update_snp(snp, + total_copy_score, + best_copy_snp->total_offset, + best_copy_snp, + &local_m, + flags_proto); + } + } + /* end of copy optimization */ + } + + /* check if we can do rle */ + snp = &sn_arr[len]; + if(best_rle_snp == NULL || + snp->index + max_sequence_length < best_rle_snp->index || + snp->index + ctx->rle_r[snp->index] < best_rle_snp->index) + { + /* best_rle_snp can't be reached by rle from snp, reset it*/ + if(ctx->rle[snp->index] > 0) + { + best_rle_snp = snp; + LOG(LOG_DEBUG, ("resetting best_rle at index %d, len %d\n", + snp->index, ctx->rle[snp->index])); + } + else + { + best_rle_snp = NULL; + } + } + else if(ctx->rle[snp->index] > 0 && + snp->index + ctx->rle_r[snp->index] >= best_rle_snp->index) + { + float best_rle_score; + float total_best_rle_score; + float snp_rle_score; + float total_snp_rle_score; + struct match rle_m; + + LOG(LOG_DEBUG, ("challenger len %d, index %d, " + "ruling len %d, index %d\n", + ctx->rle_r[snp->index], snp->index, + ctx->rle_r[best_rle_snp->index], + best_rle_snp->index)); + + /* snp and best_rle_snp is the same rle area, + * let's see which is best */ + rle_m.len = ctx->rle[best_rle_snp->index]; + rle_m.offset = 1; + best_rle_score = f(&rle_m, emd, best_rle_snp->latest_offset, NULL); + total_best_rle_score = best_rle_snp->total_score + + best_rle_score; + + rle_m.len = ctx->rle[snp->index]; + rle_m.offset = 1; + snp_rle_score = f(&rle_m, emd, snp->latest_offset, NULL); + total_snp_rle_score = snp->total_score + snp_rle_score; + + if(total_snp_rle_score <= total_best_rle_score) + { + /* yes, the snp is a better rle than best_rle_snp */ + LOG(LOG_DEBUG, ("prospect len %d, index %d, (%0.1f+%0.1f) " + "ruling len %d, index %d (%0.1f+%0.1f)\n", + ctx->rle[snp->index], snp->index, + snp->total_score, snp_rle_score, + ctx->rle[best_rle_snp->index], + best_rle_snp->index, + best_rle_snp->total_score, best_rle_score)); + best_rle_snp = snp; + LOG(LOG_DEBUG, ("setting current best_rle: " + "index %d, len %d\n", + snp->index, rle_m.len)); + } + } + if(best_rle_snp != NULL && best_rle_snp != snp) + { + float rle_score; + float total_rle_score; + /* check if rle is better */ + struct match local_m; + local_m.len = best_rle_snp->index - snp->index; + local_m.offset = 1; + + rle_score = f(&local_m, emd, best_rle_snp->latest_offset, NULL); + total_rle_score = best_rle_snp->total_score + rle_score; + + LOG(LOG_DEBUG, ("comparing index %d (%0.1f) with " + "rle index %d, len %d, total score %0.1f %0.1f\n", + snp->index, snp->total_score, + best_rle_snp->index, local_m.len, + best_rle_snp->total_score, rle_score)); + + if(snp->total_score > total_rle_score) + { + /*here it is good to do rle instead of crunch */ + LOG(LOG_DEBUG, + ("rle index %d, len %d, total %0.1f, rle %0.1f\n", + snp->index, local_m.len, + snp->total_score, total_rle_score)); + + update_snp(snp, + total_rle_score, + best_rle_snp->total_offset + 1, + best_rle_snp, + &local_m, + flags_proto); + } + } + /* end of rle optimization */ + + if (len == 0) + { + break; + } + mp = matches_get(ctx, len - 1); + LOG(LOG_DUMP, + ("matches for index %d with total score %0.1f\n", + len - 1, snp->total_score)); + + prev_score = sn_arr[len].total_score; + latest_offset_sum = sn_arr[len].total_offset; + while (mp != NULL) + { + const struct match *next; + int end_len; + struct match tmp; + int bucket_len_start; + float score; + struct search_node *prev_snp; + + next = mp->next; + end_len = 1; + tmp = *mp; + tmp.next = NULL; + bucket_len_start = 0; + prev_snp = &sn_arr[len]; + + for(tmp.len = mp->len; tmp.len >= end_len; --(tmp.len)) + { + float total_score; + unsigned int total_offset; + struct encode_match_buckets match_buckets = {{0, 0}, {0, 0}}; + + LOG(LOG_DUMP, ("mp[%d, %d], tmp[%d, %d]\n", + mp->offset, mp->len, + tmp.offset, tmp.len)); + if (bucket_len_start == 0 || + tmp.len < 4 || + tmp.len < bucket_len_start || + (skip_len0123_mirrors && tmp.len > 255 && + (tmp.len & 255) < skip_len0123_mirrors)) + { + score = f(&tmp, emd, prev_snp->latest_offset, + &match_buckets); + bucket_len_start = match_buckets.len.start; + } + + total_score = prev_score + score; + total_offset = latest_offset_sum + tmp.offset; + snp = &sn_arr[len - tmp.len]; + + LOG(LOG_DUMP, + ("[%05d] cmp [%05d, %05d score %.1f + %.1f] with %.1f", + len, tmp.offset, tmp.len, + prev_score, score, snp->total_score)); + + if (total_score < 100000000.0 && + (snp->match.len == 0 || + total_score < snp->total_score || + (total_score == snp->total_score && + total_offset < snp->total_offset && + (greedy || + (snp->match.len == 1 && snp->match.offset > 8) || + tmp.offset > 48 || + tmp.len > 15)))) + { + LOG(LOG_DUMP, (", replaced")); + snp->index = len - tmp.len; + + update_snp(snp, + total_score, + total_offset, + prev_snp, + &tmp, + flags_proto); + } + LOG(LOG_DUMP, ("\n")); + } + LOG(LOG_DUMP, ("tmp.len %d, ctx->rle[%d] %d\n", + tmp.len, len - tmp.len, + ctx->rle[len - tmp.len])); + + mp = next; + } + + /* slow way to get to the next node for cur */ + --len; + ++best_copy_len; + + progress_bump(&prog, len); + } + if(len > 0 && mp == NULL) + { + LOG(LOG_ERROR, ("No matches at len %d.\n", len)); + } + + progress_free(&prog); + + *result = sn_arr; +} + +void match_snp_get_enum(const struct search_node *snp, /* IN */ + struct match_snp_enum *snpe) /* IN/OUT */ +{ + snpe->startp = snp; + snpe->currp = snp; +} + +const struct match *match_snp_enum_get_next(void *match_snp_enum) +{ + struct match_snp_enum *snpe = match_snp_enum; + const struct match *val; + + if (snpe->currp == NULL) + { + restart: + val = NULL; + snpe->currp = snpe->startp; + } + else + { + val = &snpe->currp->match; + if (val->len == 0) + { + /* restart here too */ + goto restart; + } + snpe->currp = snpe->currp->prev; + } + return val; +} diff --git a/loader/tools/exomizer-3.1/src/search.h b/loader/tools/exomizer-3.1/src/search.h new file mode 100644 index 0000000..7934845 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/search.h @@ -0,0 +1,117 @@ +#ifndef ALREADY_INCLUDED_SEARCH +#define ALREADY_INCLUDED_SEARCH +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2002 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "match.h" +#include "output.h" + +struct encode_int_bucket { + unsigned int start; + unsigned int end; +}; + +struct encode_match_buckets { + struct encode_int_bucket len; + struct encode_int_bucket offset; +}; + +struct search_node { + int index; + struct match match; + unsigned int total_offset; + float total_score; + struct search_node *prev; + unsigned int latest_offset; +}; + +struct encode_match_data { + struct output_ctx *out; + void *priv; +}; + +/* example of what may be used for priv data + * field in the encode_match_data struct */ +typedef float encode_int_f(int val, void *priv, + struct output_ctx *out, + struct encode_int_bucket *eibp); /* IN */ + +struct encode_match_priv { + int flags_proto; + int flags_notrait; + int lit_num; + int seq_num; + int rle_num; + float lit_bits; + float seq_bits; + float rle_bits; + + encode_int_f *offset_f; + encode_int_f *len_f; + void *offset_f_priv; + void *len_f_priv; + + struct output_ctx *out; +}; + +/* end of example */ + +typedef float encode_match_f(const struct match *mp, + struct encode_match_data *emd, /* IN */ + unsigned int prev_offset, + struct encode_match_buckets *embp); /* OUT */ + +void search_node_dump(const struct search_node *snp); /* IN */ + +/* Dont forget to free the (*result) after calling */ +void search_buffer(struct match_ctx *ctx, /* IN */ + encode_match_f * f, /* IN */ + struct encode_match_data *emd, /* IN */ + int flags_proto, /* IN */ + int flags_notrait, /* IN */ + int max_sequence_length, /* IN */ + int greedy, /* IN */ + struct search_node **result); /* IN */ + +struct match_snp_enum { + const struct search_node *startp; + const struct search_node *currp; +}; + +void match_snp_get_enum(const struct search_node *snp, /* IN */ + struct match_snp_enum *snpe); /* IN/OUT */ + +const struct match *match_snp_enum_get_next(void *matchp_snp_enum); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/sfxdecr.s b/loader/tools/exomizer-3.1/src/sfxdecr.s new file mode 100644 index 0000000..1084dcf --- /dev/null +++ b/loader/tools/exomizer-3.1/src/sfxdecr.s @@ -0,0 +1,2324 @@ +; +; Copyright (c) 2002 - 2018 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software, alter it and re- +; distribute it freely for any non-commercial, non-profit purpose subject to +; the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; -- r_start_addr, done /* required, -2=basic start */ +; -- r_target, done /* required, 1, 20, 23, 52, 55, 4, 64, 128, $a2 or $a8 */ +; -- r_in_load, done /* required, load address of decrunched data area */ +; -- r_in_len, done /* required, length of decrunched data area */ +; -- i_literal_sequences_used, done /* defined if true, otherwise not */ +; -- i_max_sequence_length_256, done /* defined if true, otherwise not */ +; -- i_reuse_offset, done /* defined if true, otherwise not */ +; -- i_ram_enter, done /* undef=c_rom_config_value */ +; -- i_irq_enter, done /* undef=on, 0=off, 1=on */ +; -- i_ram_during, done /* undef=auto +; -- i_irq_during, done /* undef=auto, 0=off, 1=on */ +; -- i_ram_exit, done /* undef=auto +; -- i_irq_exit, done /* undef=auto, 0=off, 1=on */ +; -- i_nmi_during +; -- i_nmi_enter +; -- i_nmi_exit +; -- i_effect, done /* -1=none, 0(default)=lower right, 1-3=border */ +; -- i_effect_speed, done /* undef or 0=slow, otherwise fast */ +; -- i_table_addr, done /* undef=$0334 or if(r_target == 128) $0b00 */ +; -- i_fourth_offset_table, done /* defined if true, otherwise not */ +; -- i_load_addr, done /* optional, if set no basic stub, loads to value */ +; -- i_raw, done /* optional, if defined and != 0 outfile is raw */ +; -- i_perf, done [-1 (slow/short), 0(default), 1, 2(fast/large) +; -- the crunched file to it. +; ------------------------------------------------------------------- +; - if basic start -------------------------------------------------- +; -- i_basic_txt_start /* will be set before basic start, if defined */ +; -- i_basic_var_start /* will be set before basic start, if defined */ +; -- i_basic_highest_addr /* will be set before basic start, if defined */ +; ------------------------------------------------------------------- + +.IF(!.DEFINED(r_target)) + .ERROR("Required symbol r_target not defined.") +.ENDIF +.IF(!.DEFINED(i_effect)) + i_effect = 0 +.ENDIF +.IF(!.DEFINED(i_perf)) + i_perf = 0 +.ENDIF + +.IF(.DEFINED(i_irq_enter) && i_irq_enter != 0 && i_irq_enter != 1) + .ERROR("Symbol i_irq_enter must be undefined, 0 or 1.") +.ENDIF + +.IF(.DEFINED(i_irq_during) && i_irq_during != 0 && i_irq_during != 1) + .ERROR("Symbol i_irq_during must be undefined, 0 or 1.") +.ENDIF + +.IF(.DEFINED(i_irq_exit) && i_irq_exit != 0 && i_irq_exit != 1) + .ERROR("Symbol i_irq_exit must be undefined, 0 or 1.") +.ENDIF + +.IF(.DEFINED(i_load_addr) && (i_load_addr < 0 || i_load_addr > 65535)) + .ERROR("Symbol i_load_addr must be undefined or [0 or 65535].") +.ENDIF + +.IF(r_target == 1) + zp_len_lo = $80 + zp_len_hi = $81 + zp_src_lo = $82 + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $84 + zp_ro_state = $85 + + c_basic_start = $0501 + c_end_of_mem_rom = $c000 + c_effect_color = $bfdf + c_border_color = $bfdf + c_rom_config_value = 0 + c_ram_config_value = 1 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $b800 +.ELIF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55 || + r_target == 64 || r_target == 128 || r_target == 4032) + zp_len_lo = $9e + zp_len_hi = $9f + zp_src_lo = $ae + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $a7 + zp_ro_state = $a8 + + .IF(r_target == 20) + c_basic_start = $1001 + c_end_of_mem_rom = $2000 + c_effect_char = $1ff9 + c_effect_color = $97f9 + c_border_color = $900f + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 23) + c_basic_start = $0401 + c_end_of_mem_rom = $2000 + c_effect_char = $1ff9 + c_effect_color = $97f9 + c_border_color = $900f + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 52) + c_basic_start = $1201 + c_end_of_mem_rom = $8000 + c_effect_char = $11f9 + c_effect_color = $95f9 + c_border_color = $900f + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 55) + c_basic_start = $1201 + c_end_of_mem_rom = $8000 + c_effect_char = $11f9 + c_effect_color = $95f9 + c_border_color = $900f + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 64) + c_basic_start = $0801 + c_end_of_mem_rom = $a000 + c_effect_char = $07e7 + c_effect_color = $dbe7 + c_border_color = $d020 + c_rom_config_value = $37 + c_ram_config_value = $38 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 128) + c_basic_start = $1c01 + c_end_of_mem_rom = $4000 + c_effect_char = $07e7 + c_effect_color = $dbe7 + c_border_color = $d020 + c_rom_config_value = $00 + c_ram_config_value = $3f + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0b00 + .ELIF(r_target == 4032) + c_basic_start = $0401 + c_end_of_mem_rom = $8000 + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $027a + .ENDIF +.ELIF(r_target == 16 || r_target == 4) + zp_len_lo = $d8 + zp_len_hi = $d9 + zp_src_lo = $da + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $dc + zp_ro_state = $dd + + .IF(r_target == 16) + c_basic_start = $1001 + c_end_of_mem_rom = $8000 + c_effect_char = $0fe7 + c_effect_color = $0be7 + c_border_color = $ff19 + c_rom_config_value = 0 + c_ram_config_value = 1 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ELIF(r_target == 4) + c_basic_start = $1001 + c_end_of_mem_rom = $8000 + c_effect_char = $0fe7 + c_effect_color = $0be7 + c_border_color = $ff19 + c_rom_config_value = 0 + c_ram_config_value = 1 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 + .ENDIF +.ELIF(r_target == $a2) + ; http://apple2.org.za/gswv/a2zine/faqs/csa2pfaq.html#017 + zp_len_lo = $eb + zp_len_hi = $ec + zp_src_lo = $ed + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $ef + zp_ro_state = $fa + + c_basic_start = $0801 + c_end_of_mem_rom = $9600 + c_effect_color = $07f7 + c_border_color = $07f7 + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0334 +.ELIF(r_target == $a8) + zp_len_lo = $f7 + zp_len_hi = $fc + zp_src_lo = $f9 + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $f8 + zp_ro_state = $f9 + + c_end_of_mem_rom = $a000 + c_effect_color = $d017 + c_border_color = $d01a + c_rom_config_value = $ff + c_ram_config_value = $fe + c_rom_nmi_value = $40 + c_ram_nmi_value = 0 + c_default_table = $0600 +.ELIF(r_target == $bbcb) + zp_len_lo = $70 + zp_len_hi = $71 + zp_src_lo = $72 + zp_src_hi = zp_src_lo + 1 + zp_bits_hi = $74 + zp_ro_state = $75 + + c_basic_start = $1900 + c_end_of_mem_rom = $7c00 + c_effect_char = $7fe7 + c_effect_color = $7fe6 + c_border_color = $7fe7 + c_rom_config_value = 0 + c_ram_config_value = 0 + c_rom_nmi_value = 0 + c_ram_nmi_value = 0 + c_default_table = $0a34 +.ELSE + .ERROR("Symbol r_target_addr has an invalid value.") +.ENDIF + +v_safety_addr = .INCWORD("crunched_data", 0) +transfer_len ?= 0 + +.IF(i_effect == 0 && (r_target == 16 || r_target == 4 || r_target == $a2) && + (r_in_load < c_effect_color + 1 && + r_in_load + r_in_len > c_effect_color || + v_safety_addr < c_effect_color + 1 && + v_safety_addr + transfer_len > c_effect_color)) + ;; disable default effect if the address is in the + ;; data area for the c64/plus4 and Apple targets. + i_effect2 = -1 +.ELSE + i_effect2 = i_effect +.ENDIF + +.IF(!.DEFINED(i_table_addr)) + i_table_addr = c_default_table +.ENDIF + +.IF(!.DEFINED(i_ram_enter)) + i_ram_enter = c_rom_config_value +.ENDIF + +.IF(!.DEFINED(i_nmi_enter)) + i_nmi_enter = c_rom_nmi_value +.ENDIF + +.IF(!.DEFINED(i_irq_enter)) + i_irq_enter = 1 +.ENDIF + +.IF(!.DEFINED(i_irq_during)) + .IF((r_target == 16 || r_target == 4) && + (r_in_load < $0800 || + v_safety_addr < $0800 || + i_table_addr < $0800 && i_table_addr != c_default_table)) + ;; must disable irq on plus4/c16 to access mem < $0800 + i_irq_during = 0 + .IF(!.DEFINED(i_irq_exit)) + i_irq_exit = 0 + .ENDIF + .ELIF((r_target == 20 || r_target == 23 || r_target == 52 || + r_target == 55 || r_target == 64 || r_target == 128) && + (r_in_load < c_default_table || + v_safety_addr < c_default_table || + i_table_addr < c_default_table)) + ;; must disable irq on vic20,c64,c128 to access mem < c_default_table + i_irq_during = 0 + .IF(!.DEFINED(i_irq_exit)) + i_irq_exit = 0 + .ENDIF + .ENDIF +.ENDIF + +.IF(!.DEFINED(i_ram_exit)) + i_ram_exit = c_rom_config_value +.ENDIF + +.IF(!.DEFINED(i_nmi_exit)) + i_nmi_exit = c_rom_nmi_value +.ENDIF + +.IF(!.DEFINED(i_irq_exit)) + .IF(i_ram_exit != i_ram_enter) + i_irq_exit = 0 + .ELSE + i_irq_exit = i_irq_enter + .ENDIF +.ENDIF + +.IF(!.DEFINED(r_start_addr)) + .ERROR("Required symbol r_start_addr not defined.") +.ENDIF + +.IF(r_start_addr == -2 && i_ram_exit != c_rom_config_value) + .ERROR("Basic start and non-ROM configuration can't be combined.") +.ENDIF + +.IF(.DEFINED(i_line_number) && .DEFINED(i_load_addr)) + .ERROR("Basic line number and load address can't be combined.") +.ENDIF + +.IF(!.DEFINED(i_line_number)) + i_line_number = 31 +.ENDIF + +.IF(.DEFINED(i_fourth_offset_table)) +encoded_entries = 68 +.ELSE +encoded_entries = 52 +.ENDIF +; ------------------------------------------------------------------- +; -- validate some input parameters --------------------------------- +; ------------------------------------------------------------------- +.IF(r_target == 1 || r_target == 16 || r_target == 4) + .IF(i_ram_exit != 0 && i_ram_exit != 1) + .ERROR("Symbol i_ram_exit must have a value [0-1].") + .ENDIF + .IF(i_ram_enter != 0 && i_ram_enter != 1) + .ERROR("Symbol i_ram_enter must have a value [0-1].") + .ENDIF +.ELIF(r_target == 64) + .IF(i_ram_exit > $38 || i_ram_exit < $34) + .ERROR("Symbol i_ram_exit must be undefined or be within [$34-$38].") + .ENDIF + .IF(i_ram_enter > $38 || i_ram_enter < $34) + .ERROR("Symbol i_ram_enter must be undefined or be within [$34-$38].") + .ENDIF +.ELIF(r_target == 128) + .IF(i_ram_exit > $3f || i_ram_exit < 0) + .ERROR("Symbol i_ram_exit must be undefined or be within [0-$3f].") + .ENDIF + .IF(i_ram_enter > $3f || i_ram_enter < 0) + .ERROR("Symbol i_ram_enter must be undefined or be within [0-$3f].") + .ENDIF +.ENDIF + +; ------------------------------------------------------------------- +; -- convert $0 to $10000 but leave $1 - $ffff ---------------------- +; ------------------------------------------------------------------- +v_highest_addr = (.INCWORD("crunched_data", -2) + 65535) % 65536 + 1 + +; ------------------------------------------------------------------- +; -- file2_start_hook and stage2_exit_hook -------------------------- +; ------------------------------------------------------------------- +.IF(!.DEFINED(i_effect_custom) && i_effect2 == 0 && .DEFINED(c_effect_char)) +file2_start_hook = 1 + .MACRO("file2_start_hook") + .IF(v_safety_addr < file2start && ; if we are transferring anyhow + c_effect_char < v_safety_addr && + (file2start - c_effect_char < 257 || + file2start - v_safety_addr > 256) && + (i_table_addr + 3 * encoded_entries < c_effect_char || + i_table_addr > v_highest_addr)) +raw_transfer_len = file2start - c_effect_char +lowest_addr = c_effect_char + .ENDIF + .ENDMACRO +stage2_exit_hook = 1 + .MACRO("stage2_exit_hook") + .IF(c_effect_char < lowest_addr || c_effect_char > v_highest_addr) + .IF(r_target == $bbcb) + sty c_effect_char + .ELSE + stx c_effect_char + .ENDIF + .ENDIF + .ENDMACRO +.ENDIF + +; ------------------------------------------------------------------- +; -- calculate "*_during" parameters -------------------------------- +; ------------------------------------------------------------------- +.IF(!.DEFINED(i_ram_during)) + .IF(v_highest_addr > c_end_of_mem_rom) + i_ram_during = c_ram_config_value + .ELSE + i_ram_during = i_ram_enter + .ENDIF +.ENDIF + +.IF(!.DEFINED(i_nmi_during)) + .IF(v_highest_addr > c_end_of_mem_rom) + i_nmi_during = c_ram_nmi_value + .ELSE + i_nmi_during = i_nmi_enter + .ENDIF +.ENDIF +.IF(!.DEFINED(i_irq_during)) + .IF(i_irq_enter==i_irq_exit && i_ram_during==i_ram_enter && r_target!=1) + i_irq_during = i_irq_enter + .ELSE + i_irq_during = 0 + .ENDIF +.ENDIF + +.IF(r_target == 16 || r_target == 4) + .IF(i_ram_during != 0 && i_ram_during != 1) + .ERROR("Symbol i_ram_during must have a value [0-1].") + .ENDIF +.ELIF(r_target == 64) + .IF(i_ram_during > $38 || i_ram_during < $34) + .ERROR("Symbol i_ram_during must be undefined or be within [$34-$38].") + .ENDIF +.ELIF(r_target == 128) + .IF(i_ram_during > $3f || i_ram_during < 0) + .ERROR("Symbol i_ram_during must be undefined or be within [0-$3f].") + .ENDIF +.ENDIF + +; ------------------------------------------------------------------- +; -- The decruncher enter macro definition -------------------------- +; ------------------------------------------------------------------- +enter_hook = 1 +.MACRO("enter_hook") + .IF(i_irq_during != i_irq_enter) + .IF(i_irq_during == 1) + cli + .ELSE + sei + .ENDIF + .ENDIF + .IF(i_nmi_during != i_nmi_enter) + .INCLUDE("b2d_nmi") + .ENDIF + .IF(.DEFINED(i_enter_custom)) + .INCLUDE("enter_custom") + .ENDIF + .IF(i_ram_during != i_ram_enter) + .INCLUDE("b2d_ram") + .ENDIF +.ENDMACRO +; ------------------------------------------------------------------- +; -- The decruncher exit macro definition --------------------------- +; ------------------------------------------------------------------- +exit_hook = 1 +.MACRO("exit_hook") + .IF(i_ram_exit != i_ram_during) + .INCLUDE("d2r_ram") + .ENDIF + .IF(.DEFINED(i_exit_custom)) + .INCLUDE("exit_custom") + .ENDIF + .IF(i_nmi_exit != i_nmi_during) + .INCLUDE("d2r_nmi") + .ENDIF + .IF(i_irq_exit != i_irq_during) + .IF(i_irq_exit == 1) + cli + .ELSE + sei + .ENDIF + .ENDIF + .IF(r_start_addr == -2) + .IF(r_target == $a2) + .IF(.DEFINED(i_basic_txt_start)) + lda #i_basic_txt_start % 256 + sta <$67 + lda #i_basic_txt_start / 256 + sta <$68 + .ENDIF + .IF(.DEFINED(i_basic_var_start)) + lda #i_basic_var_start % 256 + sta <$69 + lda #i_basic_var_start / 256 + sta <$6a + .ENDIF + .IF(.DEFINED(i_basic_highest_addr)) + lda #(i_basic_highest_addr - 1) % 256 + sta <$73 + lda #(i_basic_highest_addr - 1) / 256 + sta <$74 + .ENDIF + .ELIF(r_target == $a8) + .ERROR("Atari target can't handle basic start.") + .ELIF(r_target == $bbcb) + .ERROR("BBC Micro B target can't handle basic start.") + .ELIF(r_target == 1) + .IF(.DEFINED(i_basic_txt_start)) + lda #i_basic_txt_start % 256 + sta <$9a + lda #i_basic_txt_start / 256 + sta <$9b + .ENDIF + .IF(.DEFINED(i_basic_var_start)) + lda #i_basic_var_start % 256 + sta <$9c + lda #i_basic_var_start / 256 + sta <$9d + .ENDIF + .IF(.DEFINED(i_basic_highest_addr)) + lda #(i_basic_highest_addr - 1) % 256 + sta <$a6 + lda #(i_basic_highest_addr - 1) / 256 + sta <$a7 + .ENDIF + .ELIF(r_target == 128) + .IF(.DEFINED(i_basic_txt_start)) + lda #i_basic_txt_start % 256 + sta <$2d + lda #i_basic_txt_start / 256 + sta <$2e + .ENDIF + .IF(.DEFINED(i_basic_var_start)) + lda #i_basic_var_start % 256 + sta $1210 + lda #i_basic_var_start / 256 + sta $1211 + .ENDIF + .IF(.DEFINED(i_basic_highest_addr)) + lda #i_basic_highest_addr % 256 + sta $1212 + lda #i_basic_highest_addr / 256 + sta $1213 + .ENDIF + .ELSE + .IF(.DEFINED(i_basic_txt_start)) + lda #i_basic_txt_start % 256 + sta <$2b + lda #i_basic_txt_start / 256 + sta <$2c + .ENDIF + .IF(.DEFINED(i_basic_var_start)) + lda #i_basic_var_start % 256 + sta <$2d + lda #i_basic_var_start / 256 + sta <$2e + .ENDIF + .IF(.DEFINED(i_basic_highest_addr)) + lda #i_basic_highest_addr % 256 + sta <$37 + lda #i_basic_highest_addr / 256 + sta <$38 + .ENDIF + .ENDIF + .IF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55) + jsr $c659 ; init + jsr $c533 ; regenerate line links + jmp $c7ae ; start + .ELIF(r_target == 4032) + jsr $B622 ; Reset TXTPTR + jsr $B5F0 ; CLR + jsr $B4B6 ; relink + jmp $B74A + .ELIF(r_target == 16 || r_target == 4) + jsr $8bbe ; init + jsr $8818 ; regenerate line links and set $2d/$2e + jsr $f3b5 ; regen color table at $0113 + jmp $8bdc ; start + .ELIF(r_target == 64) + jsr $a659 ; init + jsr $a533 ; regenerate line links + jmp $a7ae ; start + .ELIF(r_target == 128) + jsr $5ab5 ; init + jsr $4f4f ; regenerate line links and set $1210/$1211 + jmp $4af6 ; start + .ELIF(r_target == $a2) + lda #a2relinked % 256 ; set kbd vector + sta <$36 + lda #a2relinked / 256 + sta <$37 + jmp $d4f2 ; relink, exits by kbd vector +a2relinked: + jsr $fe89 ; reset kbd vector at $36/$37 + jmp $d566 ; start + .ELIF(r_target == 1) + bit $fffc + bmi oric_ROM11 + jsr $c56f ; regenerate line links + jmp $c733 ; start +oric_ROM11: + jsr $c55f ; regenerate line links + jmp $c708 ; start + .ENDIF + .ELSE + jmp r_start_addr + .ENDIF +.ENDMACRO + +; ------------------------------------------------------------------- +; -- The decrunch effect macro definition --------------------------- +; ------------------------------------------------------------------- +.IF(!.DEFINED(i_effect_speed) || i_effect_speed == 0) + slow_effect_hook = 1 +.ELSE + fast_effect_hook = 1 +.ENDIF +.MACRO("effect_hook") + .IF(.DEFINED(i_effect_custom)) + .INCLUDE("d2io") + .INCLUDE("effect_custom") + .INCLUDE("io2d") + .ELIF(i_effect2 != -1) + .INCLUDE("d2io") + .IF(i_effect2 == 0) + .IF(r_target == 16 || r_target == 4) + lda <$fd,x + sta c_effect_color + .ELIF(r_target == 1) + lda <$fd,x + and #$07 + ora #$10 + sta c_effect_color + .ELIF(r_target == $bbcb) + txa + and #$1f + ora #$80 + sta c_effect_color + .ELSE + stx c_effect_color + .ENDIF + .ELIF(i_effect2 == 1) + .IF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55) + and #$07 + ora #$18 + .ELIF(r_target == 16 || r_target == 4) + ora #$30 + .ENDIF + sta c_border_color + .ELIF(i_effect2 == 2) + .IF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55) + txa + and #$07 + ora #$18 + sta c_border_color + .ELIF(r_target == 16 || r_target == 4) + lda <$fd,x + sta c_border_color + .ELSE + stx c_border_color + .ENDIF + .ELIF(i_effect2 == 3) + .IF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55) + tya + and #$07 + ora #$18 + sta c_border_color + .ELIF(r_target == 16 || r_target == 4) + sty c_border_color + .ELSE + sty c_border_color + .ENDIF + .ELSE + .ERROR("Unknown decrunch effect.") + .ENDIF + .INCLUDE("io2d") + .ENDIF +.ENDMACRO + +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for decrunching ---------------------- +; ------------------------------------------------------------------- +.IF(r_target == 1) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for Oric-1 --------------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .IF(i_ram_during == c_ram_config_value) + lda #$84 + sta $0314 ; -- %1xxxxx0x -- RAM at $c000-$10000 ------------- + .ELSE + lda #$86 + sta $0314 ; -- %xxxxxx1x -- ROM at $c000-$10000 ------------- + .ENDIF + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + .IF(i_ram_exit == c_rom_config_value) + lda #$86 + sta $0314 ; -- %xxxxxx1x -- ROM at $c000-$10000 ------------- + .ELSE + lda #$84 + sta $0314 ; -- %1xxxxx0x -- RAM at $c000-$10000 ------------- + .ENDIF + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == 64) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for c64 ------------------------------ +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .IF(i_ram_during == i_ram_enter + 1) + inc <$01 + .ELIF(i_ram_during == i_ram_enter - 1) + dec <$01 + .ELSE + lda #i_ram_during + sta <$01 + .ENDIF + .ENDMACRO + .MACRO("d2io") + .IF(i_ram_during == $34 || i_ram_during == $38) + .IF(i_irq_during == 1) + sei + .ENDIF + .IF(i_ram_during == $34) + inc <$01 + .ELIF(i_ram_during == $38) + dec <$01 + .ENDIF + .ENDIF + .ENDMACRO + .MACRO("io2d") + .IF(i_ram_during == $34 || i_ram_during == $38) + .IF(i_ram_during == $34) + dec <$01 + .ELIF(i_ram_during == $38) + inc <$01 + .ENDIF + .IF(i_irq_during == 1) + cli + .ENDIF + .ENDIF + .ENDMACRO + .MACRO("d2r_ram") + .IF(i_ram_exit == i_ram_during + 1) + inc <$01 + .ELIF(i_ram_exit == i_ram_during - 1) + dec <$01 + .ELSE + lda #i_ram_exit + sta <$01 + .ENDIF + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == 128) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for c128 ----------------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + lda #i_ram_during + sta $ff00 + .ENDMACRO + .MACRO("d2io") + .IF(i_ram_during == $3f) + .IF(i_irq_during == 1) + sei + .ENDIF + .IF(i_effect2 == 1) + pha + .ENDIF + lda #c_rom_config_value + sta $ff00 + .IF(i_effect2 == 1) + pla + .ENDIF + .ENDIF + .ENDMACRO + .MACRO("io2d") + .IF(i_ram_during == $3f) + .IF(i_effect2 == 1) + pha + .ENDIF + lda #i_ram_during + sta $ff00 + .IF(i_effect2 == 1) + pla + .ENDIF + .IF(i_irq_during == 1) + cli + .ENDIF + .ENDIF + .ENDMACRO + .MACRO("d2r_ram") + lda #i_ram_exit + sta $ff00 + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == 16 || r_target == 4) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for c16/+4 --------------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .IF(i_ram_during == c_ram_config_value) + sta $ff3f + .ELSE + sta $ff3e + .ENDIF + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + .IF(i_ram_exit == c_rom_config_value) + sta $ff3e + .ELSE + sta $ff3f + .ENDIF + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55 || + r_target == 4032) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for c20 and PET 4032 ----------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == $a2) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for Apple ---------------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO +.ELIF(r_target == $a8) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for a8 ------------------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + lda #i_nmi_during + sta $d40e + .ENDMACRO + .MACRO("b2d_ram") + lda #i_ram_during + sta $d301 + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + lda #i_ram_exit + sta $d301 + .ENDMACRO + .MACRO("d2r_nmi") + lda #i_nmi_exit + sta $d40e + .ENDMACRO +.ELSE +.ELIF(r_target == $bbcb) +; ------------------------------------------------------------------- +; -- The ram/rom switch macros for BBC Micro B ---------------------- +; ------------------------------------------------------------------- + .MACRO("b2d_nmi") + .ENDMACRO + .MACRO("b2d_ram") + .ENDMACRO + .MACRO("d2io") + .ENDMACRO + .MACRO("io2d") + .ENDMACRO + .MACRO("d2r_ram") + .ENDMACRO + .MACRO("d2r_nmi") + .ENDMACRO + .ERROR("Unhandled target for macro definitions.") +.ENDIF +; ------------------------------------------------------------------- +; -- End of bank switch definitions --------------------------------- +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; -- Start of file header stuff ------------------------------------- +; ------------------------------------------------------------------- +.IF(r_target == 1) +; ------------------------------------------------------------------- +; -- Oric-1 file header stuff --------------------------------------- +; ------------------------------------------------------------------- + .IF(r_start_addr == -2) + .IF(!.DEFINED(i_raw) || i_raw == 0) + .BYTE($16,$16,$16,$24,$00,$00,$00,$c7) + .BYTE((o1_end - 1) / 256, (o1_end - 1) % 256) + .BYTE(c_basic_start / 256, c_basic_start % 256) + .BYTE(0, 0) + .ENDIF + .ORG(c_basic_start) +lowest_addr_out: + .WORD(basic_end, i_line_number) + .BYTE($bf, o1_start / 1000 % 10 + 48, o1_start / 100 % 10 + 48) + .BYTE(o1_start / 10 % 10 + 48, o1_start % 10 + 48, 0) +basic_end: + .BYTE(0,0) + .ELSE + .IF(!.DEFINED(i_raw) || i_raw == 0) + .BYTE($16,$16,$16,$24,$00,$00,$80,$c7) + .BYTE((o1_end - 1) / 256, (o1_end - 1) % 256) + .BYTE(o1_start / 256, o1_start % 256) + .BYTE(0, 0) + .ENDIF + .IF(!.DEFINED(i_load_addr)) + .ORG($0500) + .ELSE + .ORG(i_load_addr) + .ENDIF +lowest_addr_out: + .ENDIF +o1_start: +.ELIF(r_target == 20 || r_target == 23 || r_target == 52 || r_target == 55 || + r_target == 16 || r_target == 4 || r_target == 64 || r_target == 128 || + r_target == 4032) +; ------------------------------------------------------------------- +; -- Commodore file header stuff ------------------------------------ +; ------------------------------------------------------------------- + .IF(.DEFINED(i_load_addr)) + .IF(!.DEFINED(i_raw) || i_raw == 0) + .WORD(i_load_addr) + .ENDIF + .ORG(i_load_addr) +lowest_addr_out: + .ELSE + .IF(!.DEFINED(i_raw) || i_raw == 0) + .WORD(c_basic_start) + .ENDIF + .ORG(c_basic_start) +lowest_addr_out: + .WORD(basic_end, i_line_number) + .BYTE($9e, cbm_start / 1000 % 10 + 48, cbm_start / 100 % 10 + 48) + .BYTE(cbm_start / 10 % 10 + 48, cbm_start % 10 + 48, 0) +basic_end: +trqwrk ?= 2 + .IF(r_target == 16 || r_target == 4 || + r_target == 128 || (transfer_len - trqwrk) % 256 != 0) + .BYTE(0,0) +trqwrk = 2 + .ELSE +trqwrk = 0 + .ENDIF +; ------------------------------------------------------------------- +cbm_start: + .ENDIF +.ELIF(r_target == $a8) +; ------------------------------------------------------------------- +; -- Atari file header stuff ------------------------------------ +; ------------------------------------------------------------------- + .IF(!.DEFINED(i_raw) || i_raw == 0) + .WORD($FFFF, a8_start, a8_end - 1) + .ENDIF + .IF(!.DEFINED(i_load_addr)) + .ORG($2000) + .ELSE + .ORG(i_load_addr) + .ENDIF +lowest_addr_out: +a8_start: +.ELIF(r_target == $a2) +; ------------------------------------------------------------------- +; -- Apple file header stuff ------------------------------------ +; ------------------------------------------------------------------- + .IF(!.DEFINED(i_a2_file_type)) + .IF(.DEFINED(i_load_addr)) +i_a2_file_type = 6 ; binary + .ELSE +i_a2_file_type = $fc ; Applesoft basic + .ENDIF + .ENDIF + .IF(!.DEFINED(i_raw) || i_raw == 0) + .ORG(0) +as_start: + ;; prodos file, AppleSingle header + ;; http://apple2online.com/web_documents/ft_e0.0001_applesingle.pdf + ;; http://kaiser-edv.de/documents/AppleSingle_AppleDouble.pdf + .BYTE($00, $05, $16, $00) ; magic + .BYTE($00, $02, $00, $00) ; version + .BYTE($00, $00, $00, $00, $00, $00, $00, $00) ; filler + .BYTE($00, $00, $00, $00, $00, $00, $00, $00) + .BYTE($00, $02) ; number of entries + ;; data entry descriptor + .BYTE($00, $00, $00, $01) ; entry ID + .BYTE($00, $00, $00, as_data_entry - as_start) ; offset + .BYTE($00, $00, + (a2_end - a2_load) / 256, (a2_end - a2_load) % 256) ; length + ;; PRODOS entry descriptor + .BYTE($00, $00, $00, $0b) ; entry ID + .BYTE($00, $00, $00, as_prodos_entry - as_start) ; offset + .BYTE($00, $00, $00, $08) ; length +as_prodos_entry: + ;; PRODOS entry + .BYTE($00, $c3) ; access + .BYTE($00, i_a2_file_type) ; filetype + .IF(.DEFINED(i_load_addr)) + .BYTE($00, $00, + i_load_addr / 256, i_load_addr % 256) ; aux file type + .ELSE + ;; Applesoft basic file + .BYTE($00, $00, + c_basic_start / 256, c_basic_start % 256) ; aux file type + .ENDIF +as_data_entry: + .ENDIF + .IF(.DEFINED(i_load_addr)) + .ORG(i_load_addr) +lowest_addr_out: +a2_load: + .ELSE + .ORG(c_basic_start) +lowest_addr_out: +a2_load: + .WORD(basic_end, i_line_number) + .BYTE($8c, a2_start / 1000 % 10 + 48, a2_start / 100 % 10 + 48) + .BYTE(a2_start / 10 % 10 + 48, a2_start % 10 + 48, 0) +basic_end: + .IF(transfer_len % 256 != 0) + .BYTE(0,0) + .ENDIF + .ENDIF +; ------------------------------------------------------------------- +a2_start: + .IF(.DEFINED(i_a2_disable_dos)) + lda #$00 + jsr $fe95 + lda #$00 + jsr $fe8b + .ENDIF +.ELIF(r_target == $bbcb) +; -- No header at all for BBC b + .IF(.DEFINED(i_load_addr)) + .ORG(i_load_addr) + .ELSE + .ORG(c_basic_start) + .ENDIF +lowest_addr_out: +.ELSE + .ERROR("Unhandled target for file header stuff") +.ENDIF +; ------------------------------------------------------------------- +; -- End of file header stuff --------------------------------------- +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; -- Start of the actual decruncher --------------------------------- +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; -- required symbols: +; -- +; -- zp_len_lo A zerpoage location used for a byte. +; -- zp_len_hi A zerpoage location used for a byte. +; -- zp_src_lo + zp_src_hi A zeropage location used for a word. +; -- zp_bits_hi A zeropage location used for a byte. +; -- v_safety_addr +; -- i_table_addr +; -- +; -- optional symbols +; -- enter_hook must exit with y == 0. +; -- fast_effect_hook must not touch x or y. +; -- slow_effect_hook must not touch x or y. +; -- exit_hook +; -- stage2_exit_hook must exit with x == 0 and y == 0. +; -- +; -- macros: +; -- enter_hook +; -- effect_hook +; -- exit_hook +; -- stage2_exit_hook +; ------------------------------------------------------------------- + +zp_bitbuf = $fd +zp_dest_lo = zp_bitbuf + 1 ; dest addr lo +zp_dest_hi = zp_bitbuf + 2 ; dest addr hi + +; ------------------------------------------------------------------- +; -- start of stage 1 ----------------------------------------------- +; ------------------------------------------------------------------- +max_transfer_len = .INCLEN("crunched_data") - 5 + ldy #transfer_len % 256 + .IF(.DEFINED(enter_hook)) + .INCLUDE("enter_hook") + .ENDIF + tsx +cploop: + lda stage2end - 4,x + sta $0100 - 4,x + dex + bne cploop +.IF(transfer_len > 256) + ldx #transfer_len / 256 + 1 +.ENDIF +.IF(transfer_len != max_transfer_len) + jmp stage2start +.ENDIF +stage1end: +; ------------------------------------------------------------------- +; -- end of stage 1 ------------------------------------------------- +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; -- start of file part 2 ------------------------------------------- +; ------------------------------------------------------------------- +file2start: +.IF(.DEFINED(file2_start_hook)) + .INCLUDE("file2_start_hook") +.ENDIF +.IF(!.DEFINED(raw_transfer_len)) + .IF(v_safety_addr > file2start || v_highest_addr < file2start) +raw_transfer_len = 0 +lowest_addr = file2start + .ELSE +raw_transfer_len = file2start - v_safety_addr +lowest_addr = v_safety_addr + .ENDIF +.ENDIF + +.IF(raw_transfer_len > max_transfer_len) +transfer_len = max_transfer_len +.ELSE +transfer_len = raw_transfer_len +.ENDIF + .INCBIN("crunched_data", transfer_len + 2, max_transfer_len - transfer_len) +file2end: +; ------------------------------------------------------------------- +; -- end of file part 2 --------------------------------------------- +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; -- start of stage 2 ----------------------------------------------- +; ------------------------------------------------------------------- +.IF(transfer_len == 0) +stage2start: +.ELIF(transfer_len < 257) +stage2start: +copy1_loop: + .IF(transfer_len == 256) + lda file1start,y + sta lowest_addr,y + .ELSE + lda file1start - 1,y + sta lowest_addr - 1,y + .ENDIF + dey + bne copy1_loop +.ELSE + bne stage2start +copy2_loop1: + dey +lda_fixup: + lda file1start + transfer_len / 256 * 256,y +sta_fixup: + sta lowest_addr + transfer_len / 256 * 256,y +stage2start: + tya + bne copy2_loop1 + dec lda_fixup + 2 + dec sta_fixup + 2 + dex + bne copy2_loop1 +.ENDIF +; ------------------------------------------------------------------- +tabl_bi = i_table_addr +tabl_lo = i_table_addr + encoded_entries +tabl_hi = i_table_addr + 2 * encoded_entries + clc +table_gen: + tax + tya + and #$0f + sta tabl_lo,y + beq shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc tabl_lo - 1,y + sta tabl_lo,y + lda zp_bitbuf + .WORD((((v_highest_addr - 1) % 65536) / 256) * 256) ; => zp_dest_lo/hi +stage2end: + .ORG($0100) +; ------------------------------------------------------------------- +; -- start of stage 3 ----------------------------------------------- +; ------------------------------------------------------------------- +stage3start: +; ------------------------------------------------------------------- +; get bits (26 bytes) +; +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl C=0, reuse previous offset +no_reuse: +.ENDIF +; ------------------------------------------------------------------- +; here we decide what offset table to use (17(15) bytes) +; +.IF(!.DEFINED(i_max_sequence_length_256)) + sta zp_bitbuf + .WORD((((v_highest_addr - 1) % 65536) / 256) * 256) ; => zp_dest_lo/hi +stage2end: + .ORG($0100) +; ------------------------------------------------------------------- +; -- start of stage 3 ----------------------------------------------- +; ------------------------------------------------------------------- +stage3start: +; ------------------------------------------------------------------- +; get bits (26 bytes) +; +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl C=0, reuse previous offset +no_reuse: +.ENDIF +; ------------------------------------------------------------------- +; here we decide what offset table to use (17(15) bytes) +; +.IF(!.DEFINED(i_max_sequence_length_256)) + sta zp_bitbuf + .WORD((((v_highest_addr - 1) % 65536) / 256) * 256) ; => zp_dest_lo/hi +stage2end: + .ORG($0100) +; ------------------------------------------------------------------- +; -- start of stage 3 ----------------------------------------------- +; ------------------------------------------------------------------- +stage3start: +; ------------------------------------------------------------------- +; get bits (26 bytes) +; +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl C=0, no reuse + bne copy_next ; bit != 0 => C=0, reuse previous offset +.ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +exit_or_lit_seq: +.IF(.DEFINED(i_literal_sequences_used)) + beq decr_exit + jsr get_crunched_byte + .IF(!.DEFINED(i_max_sequence_length_256)) + sta zp_bitbuf + .WORD(((v_highest_addr % 65536) / 256) * 256) ; => zp_dest_lo/hi +stage2end: + .ORG($0100) +; ------------------------------------------------------------------- +; -- start of stage 3 ----------------------------------------------- +; ------------------------------------------------------------------- +stage3start: +; ------------------------------------------------------------------- +; get bits (26 bytes) +; +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl C=0, no reuse + bne copy_next ; bit != 0 => C=0, reuse previous offset +.ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +exit_or_lit_seq: +.IF(.DEFINED(i_literal_sequences_used)) + beq decr_exit + jsr get_crunched_byte + .IF(!.DEFINED(i_max_sequence_length_256)) + sta +#include +#include +#include + +static void str_free(void *a) +{ + char **str = a; + free(*str); +} + +static void row_free(void *a) +{ + struct vec *vec = a; + vec_free(vec, str_free); +} + +static void col_free(void *a) +{ + struct table_col *col = a; + free(col->title); + free(col->value_fmt); +} + +void table_init(struct table_ctx *table, int cell_padding) +{ + vec_init(&table->cols, sizeof (struct table_col)); + vec_init(&table->rows, sizeof (struct vec)); + table->cell_padding = cell_padding; +} + +void table_free(struct table_ctx *table) +{ + vec_free(&table->cols, col_free); + vec_free(&table->rows, row_free); +} + +void table_col_add(struct table_ctx *table, + const char *title, + const char *value_fmt, + int value_align_right) +{ + struct table_col *col = vec_push(&table->cols, NULL); + col->title = strdup(title); + col->value_fmt = strdup(value_fmt); + col->align_fmt = value_align_right ? "%*s|%*s%*s" : "%*s|%*s%-*s"; + col->width = strlen(title); +} + +void table_row_add(struct table_ctx *table, ...) +{ + struct vec_iterator col_i; + struct table_col *col; + struct buf buf = STATIC_BUF_INIT; + struct vec *row; + const char *val; + va_list argp; + + buf_init(&buf); + va_start(argp, table); + row = vec_push(&table->rows, NULL); + vec_init(row, sizeof (char *)); + + vec_get_iterator(&table->cols, &col_i); + while ((col = vec_iterator_next(&col_i)) != NULL) + { + int width; + const char *dup; + val = va_arg(argp, const char *); + + buf_printf(&buf, col->value_fmt, val); + dup = strdup((char*)buf_data(&buf)); + vec_push(row, &dup); + + width = buf_size(&buf); + if (col->width < width) + { + /* adjust the width */ + col->width = width; + } + buf_clear(&buf); + } + + va_end(argp); +} + +void table_header_print(struct table_ctx *table, struct buf *target) +{ + struct vec_iterator col_i; + struct table_col *col; + int pre = table->cell_padding; + int post = 0; + + vec_get_iterator(&table->cols, &col_i); + while ((col = vec_iterator_next(&col_i)) != NULL) + { + buf_printf(target, "%*s|%*s%-*s", post, "", pre, "", + col->width, col->title); + post = table->cell_padding; + } + buf_printf(target, "%*s|\n", post, ""); +} + +void table_sep_print(struct table_ctx *table, struct buf *target) +{ + struct vec_iterator col_i; + struct table_col *col; + vec_get_iterator(&table->cols, &col_i); + while ((col = vec_iterator_next(&col_i)) != NULL) + { + int i; + int width = col->width + 2 * table->cell_padding; + buf_printf(target, "|"); + for (i = 0; i < width; ++i) + { + buf_printf(target, "-"); + } + } + buf_printf(target, "|\n"); +} + +void table_rows_print(struct table_ctx *table, struct buf *target) +{ + struct vec_iterator col_i; + struct table_col *col; + struct vec_iterator row_i; + struct vec *row; + + vec_get_iterator(&table->rows, &row_i); + while ((row = vec_iterator_next(&row_i)) != NULL) + { + struct vec_iterator cell_i; + char *cell; + int pre = table->cell_padding; + int post = 0; + + vec_get_iterator(&table->cols, &col_i); + vec_get_iterator(row, &cell_i); + while ((col = vec_iterator_next(&col_i)) != NULL) + { + cell = *(char**)vec_iterator_next(&cell_i); + buf_printf(target, col->align_fmt, post, "", pre, "", + col->width, cell); + post = table->cell_padding; + } + buf_printf(target, "%*s|\n", post, ""); + } +} + +void table_buf_print(struct table_ctx *table, struct buf *target) +{ + table_header_print(table, target); + table_sep_print(table, target); + table_rows_print(table, target); +} + +void table_fprint(FILE *f, struct table_ctx *table) +{ + struct buf buf = STATIC_BUF_INIT; + table_buf_print(table, &buf); + fprintf(f, "%s", (char*)buf_data(&buf)); + buf_free(&buf); +} diff --git a/loader/tools/exomizer-3.1/src/table.h b/loader/tools/exomizer-3.1/src/table.h new file mode 100644 index 0000000..8e2a7b6 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/table.h @@ -0,0 +1,75 @@ +#ifndef ALREADY_INCLUDED_TABLE +#define ALREADY_INCLUDED_TABLE +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2020 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "vec.h" + +#define ALIGN_LEFT 0 +#define ALIGN_RIGHT 1 + +#define STATIC_TABLE_INIT(CELL_PADDING) \ + {STATIC_VEC_INIT(sizeof struct table_col), \ + STATIC_VEC_INIT(sizeof struct vec), \ + (CELL_PADDING)} + +struct table_ctx { + struct vec cols; + struct vec rows; + int cell_padding; +}; + +struct table_col { + char *title; + char *value_fmt; + const char *align_fmt; + int width; +}; + +void table_init(struct table_ctx *table, int cell_padding); +void table_free(struct table_ctx *table); + +void table_col_add(struct table_ctx *table, + const char *column_title, + const char *value_fmt, + int value_align_right); + +/* the value list must consist of const char * pointers match + * the number of columns added */ +void table_row_add(struct table_ctx *table, ...); + +void table_print(FILE *f, struct table_ctx *table); +void table_buf_print(struct table_ctx *table, struct buf *target); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/exomizer-3.1/src/vec.c b/loader/tools/exomizer-3.1/src/vec.c new file mode 100644 index 0000000..5a52772 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/vec.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2003 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "vec.h" +#include + +#define VEC_FLAG_SORTED 1 + +void vec_init(struct vec *p, size_t elsize) +{ + p->elsize = elsize; + buf_init(&p->buf); + p->flags = VEC_FLAG_SORTED; +} + +void vec_clear(struct vec *p, cb_free * f) +{ + struct vec_iterator i; + const void *d; + + vec_get_iterator(p, &i); + + if (f != NULL) + { + while ((d = vec_iterator_next(&i)) != NULL) + { + f((void*)d); + } + } + buf_clear(&p->buf); + p->flags = VEC_FLAG_SORTED; + +} + +void vec_free(struct vec *p, cb_free * f) +{ + vec_clear(p, f); + buf_free(&p->buf); +} + +int vec_size(const struct vec *p) +{ + int size; + size = buf_size(&p->buf) / p->elsize; + return size; +} + +void *vec_get(const struct vec *p, int index) +{ + char *buf = NULL; + + if(index >= 0 && index < vec_size(p)) + { + buf = (char *) buf_data(&p->buf); + buf += index * p->elsize; + } + + return (void *)buf; +} + +void *vec_set(struct vec *p, int index, const void *in) +{ + void *buf = NULL; + + if(index >= 0 && index < vec_size(p)) + { + buf = buf_replace(&p->buf, index * p->elsize, p->elsize, + in, p->elsize); + } + + return buf; +} + +void *vec_insert(struct vec *p, int index, const void *in) +{ + char *buf = NULL; + + if(index >= 0 && index <= vec_size(p)) + { + buf = buf_replace(&p->buf, index * p->elsize, 0, in, p->elsize); + } + return buf; +} + +void vec_remove(struct vec *p, int index) +{ + if(index >= 0 && index < vec_size(p)) + { + buf_replace(&p->buf, index * p->elsize, p->elsize, NULL, 0); + } +} + +void *vec_push(struct vec *p, const void *in) +{ + void *out; + out = buf_append(&p->buf, in, p->elsize); + p->flags &= ~VEC_FLAG_SORTED; + + return out; +} + +int vec_find(const struct vec *p, cb_cmp * f, const void *in) +{ + int lo; + + lo = -1; + if(p->flags & VEC_FLAG_SORTED) + { + int hi; + lo = 0; + hi = vec_size(p) - 1; + while(lo <= hi) + { + int next; + int val; + + next = (lo + hi) / 2; + val = f(in, vec_get(p, next)); + if(val == 0) + { + /* match */ + lo = -(next + 2); + break; + } + else if(val < 0) + { + hi = next - 1; + } + else + { + lo = next + 1; + } + } + } + return -(lo + 2); +} + +void *vec_find2(const struct vec *p, cb_cmp * f, const void *key) +{ + void *out = NULL; + int pos = vec_find(p, f, key); + if(pos >= 0) + { + out = vec_get(p, pos); + } + return out; +} + +int vec_insert_uniq(struct vec *p, cb_cmp * f, const void *in, void **outp) +{ + int val = 0; + void *out; + + val = vec_find(p, f, in); + if(val != -1) + { + if(val < 0) + { + /* not there */ + out = vec_insert(p, -(val + 2), in); + val = 1; + } + else + { + out = vec_get(p, val); + val = 0; + } + + if(outp != NULL) + { + *outp = out; + } + } + + return val; +} + +void vec_sort(struct vec *p, cb_cmp * f) +{ + qsort(buf_data(&p->buf), vec_size(p), p->elsize, f); + p->flags |= VEC_FLAG_SORTED; +} + + +void vec_get_iterator(const struct vec *p, struct vec_iterator *i) +{ + i->vec = p; + i->pos = 0; +} + +void *vec_iterator_next(struct vec_iterator *i) +{ + void *out; + int size = vec_size(i->vec); + if (i->pos >= size) + { + i->pos = 0; + return NULL; + } + out = vec_get(i->vec, i->pos); + i->pos += 1; + return out; +} + +void vec_fprint(FILE *f, const struct vec *a, cb_fprint *fprint) +{ + struct vec_iterator i; + int *e; + char *glue = "["; + + vec_get_iterator(a, &i); + while((e = vec_iterator_next(&i)) != NULL) + { + fprintf(f, "%s", glue); + fprint(f, e); + glue = ", "; + } + fprintf(f, "]"); +} + +int vec_equals(const struct vec *a, const struct vec *b, cb_cmp *cmp) +{ + struct vec_iterator ia; + struct vec_iterator ib; + void *ea; + void *eb; + int equal = 1; + + vec_get_iterator(a, &ia); + vec_get_iterator(b, &ib); + + while(equal) + { + ea = vec_iterator_next(&ia); + eb = vec_iterator_next(&ib); + + if(ea == NULL && eb == NULL) + { + break; + } + + if((ea == NULL) ^ (eb == NULL)) + { + equal = 0; + break; + } + equal = !cmp(ea, eb); + } + return equal; +} diff --git a/loader/tools/exomizer-3.1/src/vec.h b/loader/tools/exomizer-3.1/src/vec.h new file mode 100644 index 0000000..b295bc8 --- /dev/null +++ b/loader/tools/exomizer-3.1/src/vec.h @@ -0,0 +1,120 @@ +#ifndef ALREADY_INCLUDED_VEC +#define ALREADY_INCLUDED_VEC +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Copyright (c) 2003 - 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "callback.h" +#include "buf.h" +#include +#include + +#define STATIC_VEC_INIT(EL_SIZE) {(EL_SIZE), STATIC_BUF_INIT, 1} + +struct vec { + size_t elsize; + struct buf buf; + int flags; +}; + +struct vec_iterator { + const struct vec *vec; + int pos; +}; + +void vec_init(struct vec *p, size_t elsize); +void vec_clear(struct vec *p, cb_free * f); +void vec_free(struct vec *p, cb_free * f); + +int vec_size(const struct vec *p); + +/** + * Returns a pointer to the item at the given index or NULL if the + * index is out of bounds. + **/ +void *vec_get(const struct vec *p, int index); + +/** + * Returns a pointer to the set item or NULL if the index is out of + * bounds. + **/ +void *vec_set(struct vec *p, int index, const void *in); + +/** + * Returns a pointer to the inserted item or NULL if the index is out of + * bounds. + **/ +void *vec_insert(struct vec *p, int index, const void *in); +void vec_remove(struct vec *p, int index); + +void *vec_push(struct vec *p, const void *in); + +/** + * Gets the position where the key is stored in the vector. The vector + * needs to be sorted for this function to work. Returns the position, + * -1 on error or a negative number that can be converted to where + * it should have been if it had been inserted. insert_pos = -(val + 2) + **/ +int vec_find(const struct vec *p, cb_cmp * f, const void *key); + +/** + * Gets a pointer to the element that the key points to. + * Returns a pointer that may be NULL if not found. + **/ +void *vec_find2(const struct vec *p, cb_cmp * f, const void *key); + +/** + * Inserts the in element in its correct position in a sorted vector. + * returns 1 if insertion is successful, 0 if element is already + * present or -1 on error. If out is not NULL it will be + * dereferenced and set to the inserted or present element. + **/ +int vec_insert_uniq(struct vec *p, cb_cmp * f, const void *in, void **out); +void vec_sort(struct vec *p, cb_cmp * f); + +/** + * Gets a restarting iterator for the given vector. + **/ +void vec_get_iterator(const struct vec *p, struct vec_iterator *i); + +/** + * Gets a pointer to the next item from a resterting iterator. Returns + * NULL when all items has been enumerated. Will restart from the + * beginning if called again after returning NULL. + **/ +void *vec_iterator_next(struct vec_iterator *i); + +int vec_equals(const struct vec *a, const struct vec *b, cb_cmp *equals); +void vec_fprint(FILE *, const struct vec *a, cb_fprint *fprint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/loader/tools/lzsa/.gitignore b/loader/tools/lzsa/.gitignore new file mode 100644 index 0000000..3e55b04 --- /dev/null +++ b/loader/tools/lzsa/.gitignore @@ -0,0 +1,2 @@ +obj +lzsa diff --git a/loader/tools/lzsa/BlockFormat_LZSA1.md b/loader/tools/lzsa/BlockFormat_LZSA1.md new file mode 100644 index 0000000..5264354 --- /dev/null +++ b/loader/tools/lzsa/BlockFormat_LZSA1.md @@ -0,0 +1,65 @@ +# Block data format (LZSA1) + +Blocks encoded as LZSA1 are composed from consecutive commands. Each command follows this format: + +* token: +* optional extra literal length +* literal values +* match offset low +* optional match offset high +* optional extra encoded match length + +**token** + +The token byte is broken down into three parts: + + 7 6 5 4 3 2 1 0 + O L L L M M M M + +* L: 3-bit literals length (0-6, or 7 if extended). If the number of literals for this command is 0 to 6, the length is encoded in the token and no extra bytes are required. Otherwise, a value of 7 is encoded and extra bytes follow as 'optional extra literal length' +* M: 4-bit encoded match length (0-14, or 15 if extended). Likewise, if the encoded match length for this command is 0 to 14, it is directly stored, otherwise 15 is stored and extra bytes follow as 'optional extra encoded match length'. Except for the last command in a block, a command always contains a match, so the encoded match length is the actual match length offset by the minimum, which is 3 bytes. For instance, an actual match length of 10 bytes to be copied, is encoded as 7. +* O: set for a 2-bytes match offset, clear for a 1-byte match offset + +**optional extra literal length** + +If the literals length is 7 or more, the 'L' bits in the token form the value 7, and an extra byte follows here, with three possible types of value: + +* 0-248: the value is added to the 7 stored in the token, to compose the final literals length. For instance a length of 206 will be stored as 7 in the token + a single byte with the value of 199, as 7 + 199 = 206. +* 250: a second byte follows. The final literals value is 256 + the second byte. For instance, a literals length of 499 is encoded as 7 in the token, a byte with the value of 250, and a final byte with the value of 243, as 256 + 243 = 499. +* 249: a second and third byte follow, forming a little-endian 16-bit value. The final literals value is that 16-bit value. For instance, a literals length of 1024 is stored as 7 in the token, then byte values of 249, 0 and 4, as (4 * 256) = 1024. + +The extension byte values are chosen so that all three cases can be detected on 8-bit CPUs with a simple addition and overflow check. + +**literal values** + +Literal bytes, whose number is specified by the literals length, follow here. There can be zero literals in a command. + +Important note: for blocks that are part of a stream, the last command in a block ends here, as it always contains literals only. For raw blocks, the last command does contain the match offset and match length, see the note below for EOD detection. + +**match offset low** + +The low 8 bits of the match offset follows. + +**optional match offset high** + +If the 'O' bit (bit 7) is set in the token, the high 8 bits of the match offset follow, otherwise they are understood to be all set to 1. For instance, a short offset of 0x70 is interpreted as 0xff70. + +**important note regarding match offsets: stored as negative values** + +Note that the match offset is negative: it is added to the current decompressed location and not substracted, in order to locate the back-reference to copy. + +**optional extra encoded match length** + +If the encoded match length is 15 or more, the 'M' bits in the token form the value 15, and an extra byte follows here, with three possible types of value. + +* 0-237: the value is added to the 15 stored in the token. The final value is 3 + 15 + this byte. +* 239: a second byte follows. The final match length is 256 + the second byte. +* 238: a second and third byte follow, forming a little-endian 16-bit value. The final encoded match length is that 16-bit value. + +Again, the extension byte values are chosen so that all cases can be detected with a simple addition and overflow check on 8-bit CPUs. + +# End Of Data detection for raw blocks + +When the LZSA1 block is part of a stream (see StreamFormat.md), as previously mentioned, the block ends after the literal values of the last command, without a match offset or match length. + +However, in a raw LZSA1 block, the last command does include a 1-byte match offset (set to zero) and a match length. The match length is encoded as a long zero: the 'M' bits in the token form the value 15, then an extra match length byte is present, with the value 238 ("two match length bytes follow"). Finally, a two-byte zero match length follows, indicating the end of the block. EOD is the only time a zero match length (which normally would indicate a copy of 3 bytes) is encoded as a large 2-byte match value. This allows the EOD test to exist in a rarely used code branch. diff --git a/loader/tools/lzsa/BlockFormat_LZSA2.md b/loader/tools/lzsa/BlockFormat_LZSA2.md new file mode 100644 index 0000000..f46909a --- /dev/null +++ b/loader/tools/lzsa/BlockFormat_LZSA2.md @@ -0,0 +1,89 @@ +# Block data format (LZSA2) + +Blocks encoded as LZSA2 are composed from consecutive commands. Each command follows this format: + +* token: +* optional extra literal length +* literal values +* match offset +* optional extra encoded match length + +**token** + +The token byte is broken down into three parts: + + 7 6 5 4 3 2 1 0 + X Y Z L L M M M + +* L: 2-bit literals length (0-2, or 3 if extended). If the number of literals for this command is 0 to 2, the length is encoded in the token and no extra bytes are required. Otherwise, a value of 3 is encoded and extra nibbles or bytes follow as 'optional extra literal length' +* M: 3-bit encoded match length (0-6, or 7 if extended). Likewise, if the encoded match length for this command is 0 to 6, it is directly stored, otherwise 7 is stored and extra nibbles or bytes follow as 'optional extra encoded match length'. Except for the last command in a block, a command always contains a match, so the encoded match length is the actual match length offset by the minimum, which is 2 bytes. For instance, an actual match length of 5 bytes to be copied, is encoded as 3. +* XYZ: 3-bit value that indicates how to decode the match offset + +**optional extra literal length** + +If the literals length is 3 or more, the 'L' bits in the token form the value 3, and an extra nibble is read: + +* 0-14: the value is added to the 3 stored in the token, to compose the final literals length. +* 15: an extra byte follows + +If an extra byte follows, it can have two possible types of value: + +* 0-237: 18 is added to the value (3 from the token + 15 from the nibble), to compose the final literals length. For instance a length of 206 will be stored as 3 in the token + a nibble with the value of 15 + a single byte with the value of 188. +* 239: a second and third byte follow, forming a little-endian 16-bit value. The final literals value is that 16-bit value. For instance, a literals length of 1027 is stored as 3 in the token, a nibble with the value of 15, then byte values of 239, 3 and 4, as 3 + (4 * 256) = 1027. + +**literal values** + +Literal bytes, whose number is specified by the literals length, follow here. There can be zero literals in a command. + +Important note: for blocks that are part of a stream, the last command in a block ends here, as it always contains literals only. For raw blocks, the last command does contain the match offset and match length, see the note below for EOD detection. + +**match offset** + +The match offset is decoded according to the XYZ bits in the token + + XYZ + 00Z 5-bit offset: read a nibble for offset bits 1-4 and use the inverted bit Z of the token as bit 0 of the offset. set bits 5-15 of the offset to 1. + 01Z 9-bit offset: read a byte for offset bits 0-7 and use the inverted bit Z for bit 8 of the offset. set bits 9-15 of the offset to 1. + 10Z 13-bit offset: read a nibble for offset bits 9-12 and use the inverted bit Z for bit 8 of the offset, then read a byte for offset bits 0-7. set bits 13-15 of the offset to 1. substract 512 from the offset to get the final value. + 110 16-bit offset: read a byte for offset bits 8-15, then another byte for offset bits 0-7. + 111 repeat offset: reuse the offset value of the previous match command. + +The bit ordering and inversion helps optimize the decoder for size and speed on 8-bit CPUs. + +**important note regarding match offsets: stored as negative values** + +Note that the match offset is negative: it is added to the current decompressed location and not substracted, in order to locate the back-reference to copy. For this reason, as already indicated, unexpressed offset bits are set to 1 instead of 0. + +**optional extra encoded match length** + +If the encoded match length is 7 or more, the 'M' bits in the token form the value 7, and an extra nibble is read: + +* 0-14: the value is added to the 7 stored in the token, and then the minmatch of 2 is added, to compose the final match length. +* 15: an extra byte follows + +If an extra byte follows here, it can have two possible types of value: + +* 0-231: 24 is added to the value (7 from the token + 15 from the nibble + minmatch of 2), to compose the final match length. For instance a length of 150 will be stored as 7 in the token + a nibble with the value of 15 + a single byte with the value of 126. +* 233: a second and third byte follow, forming a little-endian 16-bit value. The final encoded match length is that 16-bit value. + +# End Of Data detection for raw blocks + +When the LZSA2 block is part of a stream (see StreamFormat.md), as previously mentioned, the block ends after the literal values of the last command, without a match offset or match length. + +However, in a raw LZSA2 block, the last command does include a 9-bit match offset (set to zero, to be ignored) and a EOD marker as the match length. The EOD match length marker is encoded as such: the 'M' bits in the token form the value 7, then a nibble with the value of 15 is present, then a single extra match length byte with the value of 232, indicating the end of the block. This allows the EOD test to exist in a rarely used code branch. + +The EOD condition can be easily checked as part of the tri-state condition when handling long matches. When 24 is added to the match byte value: +- If the byte doesn't overflow, the final match length is ready +- If the byte overflows and equals zero, the EOD marker has been hit +- Otherwise, if the overflows and doesn't equal zero, a 16-bit match length must be read. + +This tri-state test translates to only an addition and two branches on 8-bit CPUs. + +The equivalent EOD condition in literal lengths (which would be byte 238, that would overflow to exactly 0 when adding 18) is never emitted, so for size-optimized decompressors, the same code can be used to read both types of lengths. + +# Reading nibbles + +When the specification indicates that a nibble (4 bit value) must be read: + +* If there are no nibbles ready, read a byte immediately. Return the high 4 bits (bits 4-7) as the nibble and store the low 4 bits for later. Flag that a nibble is ready for next time. +* If a nibble is ready, return the previously stored low 4 bits (bits 0-3) and flag that no nibble is ready for next time. diff --git a/loader/tools/lzsa/LICENSE b/loader/tools/lzsa/LICENSE new file mode 100644 index 0000000..29b28c1 --- /dev/null +++ b/loader/tools/lzsa/LICENSE @@ -0,0 +1,3 @@ +The LZSA code is available under the Zlib license, except for src/matchfinder.c which is placed under the Creative Commons CC0 license. + +Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information. diff --git a/loader/tools/lzsa/LICENSE.cc0.md b/loader/tools/lzsa/LICENSE.cc0.md new file mode 100755 index 0000000..139c68e --- /dev/null +++ b/loader/tools/lzsa/LICENSE.cc0.md @@ -0,0 +1,43 @@ +## creative commons + +# CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +### Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. __Limitations and Disclaimers.__ + + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/loader/tools/lzsa/LICENSE.zlib.md b/loader/tools/lzsa/LICENSE.zlib.md new file mode 100755 index 0000000..e1296a1 --- /dev/null +++ b/loader/tools/lzsa/LICENSE.zlib.md @@ -0,0 +1,19 @@ +Copyright (c) 2019 Emmanuel Marty + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/loader/tools/lzsa/Makefile b/loader/tools/lzsa/Makefile new file mode 100755 index 0000000..a68456b --- /dev/null +++ b/loader/tools/lzsa/Makefile @@ -0,0 +1,40 @@ +CC=gcc +CFLAGS=-g -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc +#CFLAGS=-O3 -g -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc +OBJDIR=obj +LDFLAGS= + +$(OBJDIR)/%.o: src/../%.c + @mkdir -p '$(@D)' + $(CC) $(CFLAGS) -c $< -o $@ + +APP := lzsa + +OBJS += $(OBJDIR)/src/lzsa.o +OBJS += $(OBJDIR)/src/dictionary.o +OBJS += $(OBJDIR)/src/expand_block_v1.o +OBJS += $(OBJDIR)/src/expand_block_v2.o +OBJS += $(OBJDIR)/src/expand_context.o +OBJS += $(OBJDIR)/src/expand_inmem.o +OBJS += $(OBJDIR)/src/expand_streaming.o +OBJS += $(OBJDIR)/src/frame.o +OBJS += $(OBJDIR)/src/matchfinder.o +OBJS += $(OBJDIR)/src/shrink_block_v1.o +OBJS += $(OBJDIR)/src/shrink_block_v2.o +OBJS += $(OBJDIR)/src/shrink_context.o +OBJS += $(OBJDIR)/src/shrink_inmem.o +OBJS += $(OBJDIR)/src/shrink_streaming.o +OBJS += $(OBJDIR)/src/stream.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort_utils.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/sssort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/trsort.o + +all: $(APP) + +$(APP): $(OBJS) + $(CC) $^ $(LDFLAGS) -o $(APP) + +clean: + @rm -rf $(APP) $(OBJDIR) + diff --git a/loader/tools/lzsa/README.md b/loader/tools/lzsa/README.md new file mode 100755 index 0000000..4646c8f --- /dev/null +++ b/loader/tools/lzsa/README.md @@ -0,0 +1,99 @@ +LZSA is a collection of byte-aligned compression formats that are specifically engineered for very fast decompression on 8-bit systems. It can compress files of any size by using blocks of a maximum size of 64 Kb with block-interdependent compression and up to 64 Kb of back-references for matches. + +![Pareto frontier](pareto_graph.png) +*ZX Spectrum + +Check out [The Hollow](https://www.pouet.net/prod.php?which=81909) by Darklite and Offense, winner of the Solskogen 2019 wild compo, that uses LZSA on Z80. + +[Gabba](https://www.pouet.net/prod.php?which=83539) by Stardust ranked 2nd in the ZX Spectrum demo compo at CAFe demoparty 2019 and also used LZSA on Z80. + +[Myst Demake](http://www.deater.net/weave/vmwprod/mist/) for the Apple II by Vince Weaver, uses LZSA on 6502. + +The 8 bit guy's [Commander X16 ROM](https://github.com/commanderx16/x16-rom) uses LZSA on 6502 as well. + +[RomWBW](https://github.com/wwarthen/RomWBW) uses LZSA on Z80 for a variety of hobbyist computers. + +The popular [rasm](https://github.com/EdouardBERGE/rasm) assembler for Z80 features LZSA-compressed data sections. + +The [desolate](https://github.com/nzeemin/spectrum-desolate) game port to the ZX Spectrum uses LZSA compression on Z80. + +The LZSA compression tool uses an aggressive optimal packing strategy to try to find the sequence of commands that gives the smallest packed file that decompresses to the original while maintaining the maximum possible decompression speed. + +The compression formats give the user choices that range from decompressing faster than LZ4 on 8-bit systems with better compression, to compressing as well as ZX7 with much better decompression speed. LZSA1 is designed to replace LZ4 and LZSA2 to replace ZX7, in 8-bit scenarios. + +Compression ratio comparison between LZSA and other optimal packers, for a workload composed of ZX Spectrum and C64 files: + + Bytes Ratio Decompression speed vs. LZ4 + LZSA2 676681 52,49% <------ 75% + MegaLZ 4.89 679041 52,68% Not measured + ZX7 687133 53,30% 47,73% + LZ5 1.4.1 727107 56,40% 75% + LZSA1 735785 57,08% <------ 90% + Lizard -29 776122 60,21% Not measured + LZ4_HC -19 -B4 -BD 781049 60,59% 100% + Uncompressed 1289127 100% N/A + +Performance over well-known compression corpus files: + + Uncompressed LZ4_HC -19 -B4 -BD LZSA1 LZSA2 + Canterbury 2810784 935827 (33,29%) 850792 (30,27%) 770877 (27,43%) + Silesia 211938580 77299725 (36,47%) 73706340 (34,78%) 68928564 (32,52%) + Calgary 3251493 1248780 (38,40%) 1192123 (36,67%) 1110290 (34,15%) + Large 11159482 3771025 (33,79%) 3648393 (32,69%) 3519480 (31,54%) + enwik9 1000000000 371841591 (37,18%) 355360043 (35,54%) 334900611 (33,49%) + +As an example of LZSA1's simplicity, a size-optimized decompressor on Z80 has been implemented in 67 bytes. + +The compressor is approximately 2X slower than LZ4_HC but compresses better while maintaining similar decompression speeds and decompressor simplicity. + +The main differences between LZSA1 and the LZ4 compression format are: + +* The use of short (8-bit) match offsets where possible. The match-finder and optimizer cooperate to try and use the shortest match offsets possible. +* Shorter encoding of lengths. As blocks are maximum 64 Kb in size, lengths can only be up to 64 Kb. +* As a result of the smaller commands due to the possibly shorter match offsets, a minimum match size of 3 bytes instead of 4. The use of small matches is driven by the optimizer, and used where they provide gains. + +As for LZSA2: +* 5-bit, 9-bit, 13-bit and 16-bit match offsets, using nibble encoding +* Rep-matches +* Shorter encoding of lengths, also using nibbles +* A minmatch of 2 bytes +* No (slow) bit-packing. LZSA2 uses byte alignment in the hot path, and nibbles. + +Inspirations: + +* [LZ4](https://github.com/lz4/lz4) by Yann Collet. +* [LZ5/Lizard](https://github.com/inikep/lizard) by Przemyslaw Skibinski and Yann Collet. +* The suffix array intervals in [Wimlib](https://wimlib.net/git/?p=wimlib;a=tree) by Eric Biggers. +* ZX7 by Einar Saukas +* [apc](https://github.com/svendahl/cap) by Sven-Ã…ke Dahl +* [Charles Bloom](http://cbloomrants.blogspot.com/)'s compression blog + +License: + +* The LZSA code is available under the Zlib license. +* The match finder (matchfinder.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder. + +8-bit assembly code: + +* Z80 decompressors (size- and speed-optimized) written by [introspec](https://github.com/specke) with optimizations by [uniabis](https://github.com/uniabis) +* 6502 and 8088 size-optimized improvements by [Peter Ferrie](https://github.com/peterferrie) +* 6502 speed-optimized decompressor by [John Brandwood](https://github.com/jbrandwood) +* 8088 speed-optimized decompressor by [Jim Leonard](https://github.com/mobygamer) +* 6809 decompressors (Tandy CoCo, Thomson MO/TO, Dragon 32/64..) optimized by [Doug Masten](https://github.com/dougmasten) +* Hitachi 6309 decompressors (Tandy CoCo 3) also contributed by [Doug Masten](https://github.com/dougmasten) + +External links: + +* [i8080 decompressors](https://gitlab.com/ivagor/lzsa8080/tree/master) by Ivan Gorodetsky +* [PDP-11 decompressors](https://gitlab.com/ivagor/lzsa8080/tree/master/PDP11) also by Ivan Gorodetsky +* [MC68000 decompressors](https://github.com/tattlemuss/lz4-m68k/blob/master/src/lzsa.s) by Steven Tattersall +* [Gameboy decompressors](https://github.com/meltycode) by Meltycode, based on the Z80 code by introspec +* LZSA's page on [Pouet](https://www.pouet.net/prod.php?which=81573) + +# Compressed format + +Decompression code is provided for common 8-bit CPUs such as Z80 and 6502. However, if you would like to write your own, or understand the encoding, LZSA compresses data to a format that is fast and simple to decompress on 8-bit CPUs. It is encoded in either a stream of blocks, or as a single raw block, depending on command-line settings. The encoding is deliberately designed to avoid complicated operations on 8-bits (such as 16-bit math). + +* [Stream format](https://github.com/emmanuel-marty/lzsa/blob/master/StreamFormat.md) +* [Block encoding for LZSA1](https://github.com/emmanuel-marty/lzsa/blob/master/BlockFormat_LZSA1.md) +* [Block encoding for LZSA2](https://github.com/emmanuel-marty/lzsa/blob/master/BlockFormat_LZSA2.md) diff --git a/loader/tools/lzsa/StreamFormat.md b/loader/tools/lzsa/StreamFormat.md new file mode 100644 index 0000000..8eebb7e --- /dev/null +++ b/loader/tools/lzsa/StreamFormat.md @@ -0,0 +1,39 @@ +# Stream format + +The stream format is composed of: + +* a header +* one or more frames +* a footer + +# Header format + +The 3-bytes LZSA header contains a signature and a traits byte: + + 0 1 2 + 0x7b 0x9e 7 6 5 4 3 2 1 + V V V Z Z Z Z + <--- signature ---> <- traits -> + +Trait bits: + +* V: 3 bit code that indicates which block data encoding is used. 0 is LZSA1 and 1 is LZSA2. +* Z: these bits in the traits are set to 0 for LZSA1 and LZSA2. + +# Frame format + +Each frame contains a 3-bytes length followed by block data that expands to up to 64 Kb of decompressed data. The block data is encoded either as LZSA1 or LZSA2 depending on the V bits of the traits byte in the header. + + 0 1 2 + DSZ0 DSZ1 U|DSZ2 + +* DSZ0 (length byte 0) contains bits 0-7 of the block data size +* DSZ1 (length byte 1) contains bits 8-15 of the block data size +* DSZ2 (bit 0 of length byte 2) contains bit 16 of the block data size +* U (bit 7 of length byte 2) is set if the block data is uncompressed, and clear if the block data is compressed. +* Bits 1..6 of length byte 2 are currently undefined and must be set to 0. + +# Footer format + +The stream ends with the EOD frame: the 3 length bytes are set to 0x00, 0x00, 0x00, and no block data follows. + diff --git a/loader/tools/lzsa/VS2017/.gitignore b/loader/tools/lzsa/VS2017/.gitignore new file mode 100644 index 0000000..02032ac --- /dev/null +++ b/loader/tools/lzsa/VS2017/.gitignore @@ -0,0 +1,3 @@ +.vs +Debug +Release diff --git a/loader/tools/lzsa/VS2017/lzsa.sln b/loader/tools/lzsa/VS2017/lzsa.sln new file mode 100755 index 0000000..ffd10e3 --- /dev/null +++ b/loader/tools/lzsa/VS2017/lzsa.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.489 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lzsa", "lzsa.vcxproj", "{3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.ActiveCfg = Debug|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.Build.0 = Debug|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.ActiveCfg = Debug|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.Build.0 = Debug|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.ActiveCfg = Release|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.Build.0 = Release|x64 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.ActiveCfg = Release|Win32 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A1E1655C-AA9F-41F0-80C9-18DD0B859D7C} + EndGlobalSection +EndGlobal diff --git a/loader/tools/lzsa/VS2017/lzsa.vcxproj b/loader/tools/lzsa/VS2017/lzsa.vcxproj new file mode 100755 index 0000000..f9275dc --- /dev/null +++ b/loader/tools/lzsa/VS2017/lzsa.vcxproj @@ -0,0 +1,225 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8} + Win32Proj + lzsa + 8.1 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + true + $(ProjectDir)bin\ + $(ProjectName)_debug + + + false + $(ProjectDir)bin\ + + + false + $(ProjectDir)bin\ + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreadedDebug + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + + + Console + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreadedDebug + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + + + Console + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreaded + Speed + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + MultiThreaded + Speed + ..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(ProjectDir)bin\$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/loader/tools/lzsa/VS2017/lzsa.vcxproj.filters b/loader/tools/lzsa/VS2017/lzsa.vcxproj.filters new file mode 100755 index 0000000..415b4dd --- /dev/null +++ b/loader/tools/lzsa/VS2017/lzsa.vcxproj.filters @@ -0,0 +1,147 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {5ec09c0d-19f7-4a6f-b524-f405fb99e48c} + + + {a922f475-1322-496d-8a6d-7f1c6b92423d} + + + {bd05c6e8-af92-4ab8-8916-0424cd8d186b} + + + + + Fichiers d%27en-tête + + + Fichiers sources + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources\libdivsufsort\include + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\include + + + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources\libdivsufsort\lib + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources\libdivsufsort\lib + + + \ No newline at end of file diff --git a/loader/tools/lzsa/VS2017/lzsa.vcxproj.user b/loader/tools/lzsa/VS2017/lzsa.vcxproj.user new file mode 100755 index 0000000..58ecafc --- /dev/null +++ b/loader/tools/lzsa/VS2017/lzsa.vcxproj.user @@ -0,0 +1,27 @@ + + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + + $(TargetPath) + -f2 -c -v corpus/zxspectrum/graphics/bfox-dont_go_away_(2010).mg1 bfox.lzs + WindowsLocalDebugger + $(ProjectDir)..\ + + \ No newline at end of file diff --git a/loader/tools/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj b/loader/tools/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj new file mode 100644 index 0000000..608a7d6 --- /dev/null +++ b/loader/tools/lzsa/Xcode/lzsa.xcodeproj/project.pbxproj @@ -0,0 +1,429 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 0CADC63122AAD8EB003E9821 /* shrink_inmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */; }; + 0CADC63222AAD8EB003E9821 /* frame.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5F322AAD8EB003E9821 /* frame.c */; }; + 0CADC63322AAD8EB003E9821 /* matchfinder.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5F422AAD8EB003E9821 /* matchfinder.c */; }; + 0CADC63422AAD8EB003E9821 /* shrink_block_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */; }; + 0CADC63A22AAD8EB003E9821 /* trsort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61622AAD8EB003E9821 /* trsort.c */; }; + 0CADC63B22AAD8EB003E9821 /* divsufsort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61722AAD8EB003E9821 /* divsufsort.c */; }; + 0CADC63D22AAD8EB003E9821 /* sssort.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC61922AAD8EB003E9821 /* sssort.c */; }; + 0CADC63E22AAD8EB003E9821 /* expand_block_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */; }; + 0CADC63F22AAD8EB003E9821 /* lzsa.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62222AAD8EB003E9821 /* lzsa.c */; }; + 0CADC64022AAD8EB003E9821 /* shrink_streaming.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */; }; + 0CADC64122AAD8EB003E9821 /* expand_inmem.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62522AAD8EB003E9821 /* expand_inmem.c */; }; + 0CADC64222AAD8EB003E9821 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62922AAD8EB003E9821 /* stream.c */; }; + 0CADC64322AAD8EB003E9821 /* expand_block_v2.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */; }; + 0CADC64422AAD8EB003E9821 /* shrink_context.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62B22AAD8EB003E9821 /* shrink_context.c */; }; + 0CADC64522AAD8EB003E9821 /* expand_streaming.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */; }; + 0CADC64622AAD8EB003E9821 /* dictionary.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62E22AAD8EB003E9821 /* dictionary.c */; }; + 0CADC64722AAD8EB003E9821 /* expand_context.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC62F22AAD8EB003E9821 /* expand_context.c */; }; + 0CADC64822AAD8EB003E9821 /* shrink_block_v2.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */; }; + 0CADC64A22AB8DAD003E9821 /* divsufsort_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0CADC57622A65EA4003E9821 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0CADC57822A65EA5003E9821 /* lzsa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lzsa; sourceTree = BUILT_PRODUCTS_DIR; }; + 0CADC5ED22AAD8EA003E9821 /* expand_streaming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_streaming.h; path = ../../src/expand_streaming.h; sourceTree = ""; }; + 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_inmem.c; path = ../../src/shrink_inmem.c; sourceTree = ""; }; + 0CADC5EF22AAD8EB003E9821 /* stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stream.h; path = ../../src/stream.h; sourceTree = ""; }; + 0CADC5F022AAD8EB003E9821 /* expand_block_v1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_block_v1.h; path = ../../src/expand_block_v1.h; sourceTree = ""; }; + 0CADC5F122AAD8EB003E9821 /* shrink_block_v1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_block_v1.h; path = ../../src/shrink_block_v1.h; sourceTree = ""; }; + 0CADC5F222AAD8EB003E9821 /* lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lib.h; path = ../../src/lib.h; sourceTree = ""; }; + 0CADC5F322AAD8EB003E9821 /* frame.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = frame.c; path = ../../src/frame.c; sourceTree = ""; }; + 0CADC5F422AAD8EB003E9821 /* matchfinder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = matchfinder.c; path = ../../src/matchfinder.c; sourceTree = ""; }; + 0CADC5F522AAD8EB003E9821 /* matchfinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = matchfinder.h; path = ../../src/matchfinder.h; sourceTree = ""; }; + 0CADC5F622AAD8EB003E9821 /* dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dictionary.h; path = ../../src/dictionary.h; sourceTree = ""; }; + 0CADC5F722AAD8EB003E9821 /* shrink_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_context.h; path = ../../src/shrink_context.h; sourceTree = ""; }; + 0CADC5F822AAD8EB003E9821 /* shrink_inmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_inmem.h; path = ../../src/shrink_inmem.h; sourceTree = ""; }; + 0CADC5F922AAD8EB003E9821 /* expand_block_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_block_v2.h; path = ../../src/expand_block_v2.h; sourceTree = ""; }; + 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_block_v1.c; path = ../../src/shrink_block_v1.c; sourceTree = ""; }; + 0CADC5FB22AAD8EB003E9821 /* expand_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_context.h; path = ../../src/expand_context.h; sourceTree = ""; }; + 0CADC60922AAD8EB003E9821 /* divsufsort_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort_private.h; sourceTree = ""; }; + 0CADC60A22AAD8EB003E9821 /* divsufsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort.h; sourceTree = ""; }; + 0CADC61622AAD8EB003E9821 /* trsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trsort.c; sourceTree = ""; }; + 0CADC61722AAD8EB003E9821 /* divsufsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = divsufsort.c; sourceTree = ""; }; + 0CADC61922AAD8EB003E9821 /* sssort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sssort.c; sourceTree = ""; }; + 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_block_v1.c; path = ../../src/expand_block_v1.c; sourceTree = ""; }; + 0CADC62222AAD8EB003E9821 /* lzsa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lzsa.c; path = ../../src/lzsa.c; sourceTree = ""; }; + 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_streaming.c; path = ../../src/shrink_streaming.c; sourceTree = ""; }; + 0CADC62422AAD8EB003E9821 /* format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = format.h; path = ../../src/format.h; sourceTree = ""; }; + 0CADC62522AAD8EB003E9821 /* expand_inmem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_inmem.c; path = ../../src/expand_inmem.c; sourceTree = ""; }; + 0CADC62622AAD8EB003E9821 /* shrink_block_v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_block_v2.h; path = ../../src/shrink_block_v2.h; sourceTree = ""; }; + 0CADC62722AAD8EB003E9821 /* expand_inmem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expand_inmem.h; path = ../../src/expand_inmem.h; sourceTree = ""; }; + 0CADC62822AAD8EB003E9821 /* shrink_streaming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shrink_streaming.h; path = ../../src/shrink_streaming.h; sourceTree = ""; }; + 0CADC62922AAD8EB003E9821 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../../src/stream.c; sourceTree = ""; }; + 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_block_v2.c; path = ../../src/expand_block_v2.c; sourceTree = ""; }; + 0CADC62B22AAD8EB003E9821 /* shrink_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_context.c; path = ../../src/shrink_context.c; sourceTree = ""; }; + 0CADC62C22AAD8EB003E9821 /* frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = frame.h; path = ../../src/frame.h; sourceTree = ""; }; + 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_streaming.c; path = ../../src/expand_streaming.c; sourceTree = ""; }; + 0CADC62E22AAD8EB003E9821 /* dictionary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dictionary.c; path = ../../src/dictionary.c; sourceTree = ""; }; + 0CADC62F22AAD8EB003E9821 /* expand_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = expand_context.c; path = ../../src/expand_context.c; sourceTree = ""; }; + 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shrink_block_v2.c; path = ../../src/shrink_block_v2.c; sourceTree = ""; }; + 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = divsufsort_utils.c; sourceTree = ""; }; + 0CADC64B22AB8DC3003E9821 /* divsufsort_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = divsufsort_config.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0CADC57522A65EA4003E9821 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0CADC56F22A65EA4003E9821 = { + isa = PBXGroup; + children = ( + 0CADC57A22A65EA5003E9821 /* lzsa */, + 0CADC57922A65EA5003E9821 /* Products */, + ); + sourceTree = ""; + }; + 0CADC57922A65EA5003E9821 /* Products */ = { + isa = PBXGroup; + children = ( + 0CADC57822A65EA5003E9821 /* lzsa */, + ); + name = Products; + sourceTree = ""; + }; + 0CADC57A22A65EA5003E9821 /* lzsa */ = { + isa = PBXGroup; + children = ( + 0CADC62E22AAD8EB003E9821 /* dictionary.c */, + 0CADC5F622AAD8EB003E9821 /* dictionary.h */, + 0CADC62122AAD8EB003E9821 /* expand_block_v1.c */, + 0CADC5F022AAD8EB003E9821 /* expand_block_v1.h */, + 0CADC62A22AAD8EB003E9821 /* expand_block_v2.c */, + 0CADC5F922AAD8EB003E9821 /* expand_block_v2.h */, + 0CADC62F22AAD8EB003E9821 /* expand_context.c */, + 0CADC5FB22AAD8EB003E9821 /* expand_context.h */, + 0CADC62522AAD8EB003E9821 /* expand_inmem.c */, + 0CADC62722AAD8EB003E9821 /* expand_inmem.h */, + 0CADC62D22AAD8EB003E9821 /* expand_streaming.c */, + 0CADC5ED22AAD8EA003E9821 /* expand_streaming.h */, + 0CADC62422AAD8EB003E9821 /* format.h */, + 0CADC5F322AAD8EB003E9821 /* frame.c */, + 0CADC62C22AAD8EB003E9821 /* frame.h */, + 0CADC5F222AAD8EB003E9821 /* lib.h */, + 0CADC5FC22AAD8EB003E9821 /* libdivsufsort */, + 0CADC62222AAD8EB003E9821 /* lzsa.c */, + 0CADC5F422AAD8EB003E9821 /* matchfinder.c */, + 0CADC5F522AAD8EB003E9821 /* matchfinder.h */, + 0CADC5FA22AAD8EB003E9821 /* shrink_block_v1.c */, + 0CADC5F122AAD8EB003E9821 /* shrink_block_v1.h */, + 0CADC63022AAD8EB003E9821 /* shrink_block_v2.c */, + 0CADC62622AAD8EB003E9821 /* shrink_block_v2.h */, + 0CADC62B22AAD8EB003E9821 /* shrink_context.c */, + 0CADC5F722AAD8EB003E9821 /* shrink_context.h */, + 0CADC5EE22AAD8EA003E9821 /* shrink_inmem.c */, + 0CADC5F822AAD8EB003E9821 /* shrink_inmem.h */, + 0CADC62322AAD8EB003E9821 /* shrink_streaming.c */, + 0CADC62822AAD8EB003E9821 /* shrink_streaming.h */, + 0CADC62922AAD8EB003E9821 /* stream.c */, + 0CADC5EF22AAD8EB003E9821 /* stream.h */, + ); + path = lzsa; + sourceTree = ""; + }; + 0CADC5FC22AAD8EB003E9821 /* libdivsufsort */ = { + isa = PBXGroup; + children = ( + 0CADC60322AAD8EB003E9821 /* include */, + 0CADC61422AAD8EB003E9821 /* lib */, + ); + name = libdivsufsort; + path = ../../src/libdivsufsort; + sourceTree = ""; + }; + 0CADC60322AAD8EB003E9821 /* include */ = { + isa = PBXGroup; + children = ( + 0CADC64B22AB8DC3003E9821 /* divsufsort_config.h */, + 0CADC60922AAD8EB003E9821 /* divsufsort_private.h */, + 0CADC60A22AAD8EB003E9821 /* divsufsort.h */, + ); + path = include; + sourceTree = ""; + }; + 0CADC61422AAD8EB003E9821 /* lib */ = { + isa = PBXGroup; + children = ( + 0CADC64922AB8DAD003E9821 /* divsufsort_utils.c */, + 0CADC61622AAD8EB003E9821 /* trsort.c */, + 0CADC61722AAD8EB003E9821 /* divsufsort.c */, + 0CADC61922AAD8EB003E9821 /* sssort.c */, + ); + path = lib; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0CADC57722A65EA4003E9821 /* lzsa */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0CADC57F22A65EA5003E9821 /* Build configuration list for PBXNativeTarget "lzsa" */; + buildPhases = ( + 0CADC57422A65EA4003E9821 /* Sources */, + 0CADC57522A65EA4003E9821 /* Frameworks */, + 0CADC57622A65EA4003E9821 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = lzsa; + productName = lzsa; + productReference = 0CADC57822A65EA5003E9821 /* lzsa */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0CADC57022A65EA4003E9821 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = Emmanuel; + TargetAttributes = { + 0CADC57722A65EA4003E9821 = { + CreatedOnToolsVersion = 10.2.1; + }; + }; + }; + buildConfigurationList = 0CADC57322A65EA4003E9821 /* Build configuration list for PBXProject "lzsa" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0CADC56F22A65EA4003E9821; + productRefGroup = 0CADC57922A65EA5003E9821 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0CADC57722A65EA4003E9821 /* lzsa */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0CADC57422A65EA4003E9821 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CADC64822AAD8EB003E9821 /* shrink_block_v2.c in Sources */, + 0CADC63D22AAD8EB003E9821 /* sssort.c in Sources */, + 0CADC64322AAD8EB003E9821 /* expand_block_v2.c in Sources */, + 0CADC63F22AAD8EB003E9821 /* lzsa.c in Sources */, + 0CADC64422AAD8EB003E9821 /* shrink_context.c in Sources */, + 0CADC64522AAD8EB003E9821 /* expand_streaming.c in Sources */, + 0CADC63E22AAD8EB003E9821 /* expand_block_v1.c in Sources */, + 0CADC63122AAD8EB003E9821 /* shrink_inmem.c in Sources */, + 0CADC63B22AAD8EB003E9821 /* divsufsort.c in Sources */, + 0CADC64622AAD8EB003E9821 /* dictionary.c in Sources */, + 0CADC63422AAD8EB003E9821 /* shrink_block_v1.c in Sources */, + 0CADC64A22AB8DAD003E9821 /* divsufsort_utils.c in Sources */, + 0CADC64222AAD8EB003E9821 /* stream.c in Sources */, + 0CADC64022AAD8EB003E9821 /* shrink_streaming.c in Sources */, + 0CADC63A22AAD8EB003E9821 /* trsort.c in Sources */, + 0CADC64122AAD8EB003E9821 /* expand_inmem.c in Sources */, + 0CADC63322AAD8EB003E9821 /* matchfinder.c in Sources */, + 0CADC64722AAD8EB003E9821 /* expand_context.c in Sources */, + 0CADC63222AAD8EB003E9821 /* frame.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0CADC57D22A65EA5003E9821 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src/libdivsufsort/include, + ../src/xxhash, + ../src, + ); + LLVM_LTO = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; + SDKROOT = macosx; + }; + name = Debug; + }; + 0CADC57E22A65EA5003E9821 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../src/libdivsufsort/include, + ../src/xxhash, + ../src, + ); + LLVM_LTO = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + SDKROOT = macosx; + }; + name = Release; + }; + 0CADC58022A65EA5003E9821 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 0CADC58122A65EA5003E9821 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0CADC57322A65EA4003E9821 /* Build configuration list for PBXProject "lzsa" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CADC57D22A65EA5003E9821 /* Debug */, + 0CADC57E22A65EA5003E9821 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0CADC57F22A65EA5003E9821 /* Build configuration list for PBXNativeTarget "lzsa" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CADC58022A65EA5003E9821 /* Debug */, + 0CADC58122A65EA5003E9821 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0CADC57022A65EA4003E9821 /* Project object */; +} diff --git a/loader/tools/lzsa/asm/6502/decompress_fast_v1.asm b/loader/tools/lzsa/asm/6502/decompress_fast_v1.asm new file mode 100644 index 0000000..7aa651d --- /dev/null +++ b/loader/tools/lzsa/asm/6502/decompress_fast_v1.asm @@ -0,0 +1,305 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA1 block. Create one with lzsa -r +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +DECOMPRESS_LZSA1_FAST + LDY #$00 + +DECODE_TOKEN + JSR GETSRC ; read token byte: O|LLL|MMMM + PHA ; preserve token on stack + + AND #$70 ; isolate literals count + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$70 ; LITERALS_RUN_LEN? + BNE PREPARE_COPY_LITERALS ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$F9 ; (LITERALS_RUN_LEN) + BCC PREPARE_COPY_LITERALS_DIRECT + BEQ LARGE_VARLEN_LITERALS ; if adding up to zero, go grab 16-bit count + + JSR GETSRC ; get single extended byte of variable literals count + INY ; add 256 to literals count + BCS PREPARE_COPY_LITERALS_DIRECT ; (*like JMP PREPARE_COPY_LITERALS_DIRECT but shorter) + +LARGE_VARLEN_LITERALS ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + TXA + BCS PREPARE_COPY_LARGE_LITERALS ; (*like JMP PREPARE_COPY_LITERALS_DIRECT but shorter) + +PREPARE_COPY_LITERALS + TAX + LDA SHIFT_TABLE-1,X ; shift literals length into place + ; -1 because position 00 is reserved +PREPARE_COPY_LITERALS_DIRECT + TAX + +PREPARE_COPY_LARGE_LITERALS + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + BMI GET_LONG_OFFSET ; $80: 16 bit offset + + JSR GETSRC ; get 8 bit offset from stream in A + TAX ; save for later + LDA #$FF ; high 8 bits + BNE GOT_OFFSET ; go prepare match + ; (*like JMP GOT_OFFSET but shorter) + +SHORT_VARLEN_MATCHLEN + JSR GETSRC ; get single extended byte of variable match len + INY ; add 256 to match length + +PREPARE_COPY_MATCH + TAX +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + BEQ DECODE_TOKEN ; (*like JMP DECODE_TOKEN but shorter) + +!ifdef BACKWARD_DECOMPRESS { + +GETMATCH_ADJ_HI + DEC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} else { + +GETMATCH_ADJ_HI + INC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} + +GET_LONG_OFFSET ; handle 16 bit offset: + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + +GOT_OFFSET + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + STA OFFSHI ; store high 8 bits of offset + STX OFFSLO + + SEC ; substract dest - match offset + LDA PUTDST+1 +OFFSLO = *+1 + SBC #$AA ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + STA OFFSHI ; store high 8 bits of offset + TXA + + CLC ; add dest + match offset + ADC PUTDST+1 ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$0F ; isolate match len (MMMM) + ADC #$02 ; plus carry which is always set by the high ADC + CMP #$12 ; MATCH_RUN_LEN? + BCC PREPARE_COPY_MATCH ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; add MATCH_RUN_LEN and MIN_MATCH_SIZE to match length + BCC PREPARE_COPY_MATCH + BNE SHORT_VARLEN_MATCHLEN + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + ; large match length with zero high byte? + BNE PREPARE_COPY_MATCH_Y ; if not, continue + +DECOMPRESSION_DONE + RTS + +SHIFT_TABLE + !BYTE $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !BYTE $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 + !BYTE $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 + !BYTE $03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03 + !BYTE $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04 + !BYTE $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05 + !BYTE $06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06 + !BYTE $07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07 + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BEQ PUTDST_ADJ_HI + DEC PUTDST+1 + RTS + +PUTDST_ADJ_HI + DEC PUTDST+2 + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BEQ GETSRC_ADJ_HI + DEC GETSRC+1 + PLA + RTS + +GETSRC_ADJ_HI + DEC GETSRC+2 + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BEQ PUTDST_ADJ_HI + RTS + +PUTDST_ADJ_HI + INC PUTDST+2 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BEQ GETSRC_ADJ_HI + RTS + +GETSRC_ADJ_HI + INC GETSRC+2 + RTS +} diff --git a/loader/tools/lzsa/asm/6502/decompress_fast_v2.asm b/loader/tools/lzsa/asm/6502/decompress_fast_v2.asm new file mode 100644 index 0000000..1e49a75 --- /dev/null +++ b/loader/tools/lzsa/asm/6502/decompress_fast_v2.asm @@ -0,0 +1,359 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +NIBCOUNT = $FC ; zero-page location for temp offset + +DECOMPRESS_LZSA2_FAST + LDY #$00 + STY NIBCOUNT + +DECODE_TOKEN + JSR GETSRC ; read token byte: XYZ|LL|MMM + PHA ; preserve token on stack + + AND #$18 ; isolate literals count (LL) + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$18 ; LITERALS_RUN_LEN_V2? + BCC PREPARE_COPY_LITERALS ; if less, count is directly embedded in token + + JSR GETNIBBLE ; get extra literals length nibble + ; add nibble to len from token + ADC #$02 ; (LITERALS_RUN_LEN_V2) minus carry + CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + BCC PREPARE_COPY_LITERALS_DIRECT ; if less, literals count is complete + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; overflow? + JMP PREPARE_COPY_LITERALS_DIRECT + +PREPARE_COPY_LITERALS_LARGE + ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + BCS PREPARE_COPY_LITERALS_HIGH ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +PREPARE_COPY_LITERALS + LSR ; shift literals count into place + LSR + LSR + +PREPARE_COPY_LITERALS_DIRECT + TAX + BCS PREPARE_COPY_LITERALS_LARGE ; if so, literals count is large + +PREPARE_COPY_LITERALS_HIGH + TXA + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + ASL + BCS REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset + + ASL ; 0YZ: 5 or 9 bit offset + BCS OFFSET_9_BIT + + ; 00Z: 5 bit offset + + LDX #$FF ; set offset bits 15-8 to 1 + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 0, read nibble for bits 4-1 + ORA #$E0 ; set bits 7-5 to 1 + BNE GOT_OFFSET_LO ; go store low byte of match offset and prepare match + +OFFSET_9_BIT ; 01Z: 9 bit offset + ROL ; carry: Z bit; A: xxxxxxx1 (carry known set from BCS OFFSET_9_BIT) + ADC #$00 ; if Z bit is set, add 1 to A (bit 0 of A is now 0), otherwise bit 0 is 1 + ORA #$FE ; set offset bits 15-9 to 1. reversed Z is already in bit 0 + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_LARGE_OFFSET + ASL ; 13 bit offset? + BCS REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 8, read nibble for bits 12-9 + ADC #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_16_BIT ; rep-match or 16 bit offset + BMI REP_MATCH ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + JSR GETSRC ; grab high 8 bits +GOT_OFFSET_HI + TAX + JSR GETSRC ; grab low 8 bits +GOT_OFFSET_LO + STA OFFSLO ; store low byte of match offset + STX OFFSHI ; store high byte of match offset + +REP_MATCH +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + SEC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + SBC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + CLC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + ADC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$07 ; isolate match len (MMM) + ADC #$01 ; add MIN_MATCH_SIZE_V2 and carry + CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + BCC PREPARE_COPY_MATCH ; if less, length is directly embedded in token + + JSR GETNIBBLE ; get extra match length nibble + ; add nibble to len from token + ADC #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + BCC PREPARE_COPY_MATCH ; if less, match length is complete + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$E8 ; overflow? + +PREPARE_COPY_MATCH + TAX + BCC PREPARE_COPY_MATCH_Y ; if not, the match length is complete + BEQ DECOMPRESSION_DONE ; if EOD code, bail + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BEQ GETMATCH_ADJ_HI +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + JMP DECODE_TOKEN + +!ifdef BACKWARD_DECOMPRESS { + +GETMATCH_ADJ_HI + DEC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} else { + +GETMATCH_ADJ_HI + INC COPY_MATCH_LOOP+2 + JMP GETMATCH_DONE + +} + +GETCOMBINEDBITS + EOR #$80 + ASL + PHP + + JSR GETNIBBLE ; get nibble into bits 0-3 (for offset bits 1-4) + PLP ; merge Z bit as the carry bit (for offset bit 0) + ROL ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +DECOMPRESSION_DONE + RTS + +GETNIBBLE +NIBBLES = *+1 + LDA #$AA + LSR NIBCOUNT + BCC NEED_NIBBLES + AND #$0F ; isolate low 4 bits of nibble + RTS + +NEED_NIBBLES + INC NIBCOUNT + JSR GETSRC ; get 2 nibbles + STA NIBBLES + LSR + LSR + LSR + LSR + SEC + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BEQ PUTDST_ADJ_HI + DEC PUTDST+1 + RTS + +PUTDST_ADJ_HI + DEC PUTDST+2 + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BEQ GETSRC_ADJ_HI + DEC GETSRC+1 + PLA + RTS + +GETSRC_ADJ_HI + DEC GETSRC+2 + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BEQ PUTDST_ADJ_HI + RTS + +PUTDST_ADJ_HI + INC PUTDST+2 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BEQ GETSRC_ADJ_HI + RTS + +GETSRC_ADJ_HI + INC GETSRC+2 + RTS +} + diff --git a/loader/tools/lzsa/asm/6502/decompress_faster_v1.asm b/loader/tools/lzsa/asm/6502/decompress_faster_v1.asm new file mode 100644 index 0000000..1f65a40 --- /dev/null +++ b/loader/tools/lzsa/asm/6502/decompress_faster_v1.asm @@ -0,0 +1,353 @@ +; *************************************************************************** +; *************************************************************************** +; +; lzsa1_6502.s +; +; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA1 format. +; +; This code is written for the ACME assembler. +; +; Optional code is presented for one minor 6502 optimization that breaks +; compatibility with the current LZSA1 format standard. +; +; The code is 168 bytes for the small version, and 205 bytes for the normal. +; +; Copyright John Brandwood 2019. +; +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) +; +; *************************************************************************** +; *************************************************************************** + + + +; *************************************************************************** +; *************************************************************************** +; +; Decompression Options & Macros +; + + ; + ; Choose size over space (within sane limits)? + ; + +LZSA_SMALL_SIZE = 0 + + ; + ; Remove code inlining to save space? + ; + ; This saves 15 bytes of code at the cost of 7% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_NO_INLINE = 1 + } else { +LZSA_NO_INLINE = 0 + } + + ; + ; Use smaller code for copying literals? + ; + ; This saves 11 bytes of code at the cost of 15% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_SHORT_CP = 1 + } else { +LZSA_SHORT_CP = 0 + } + + ; + ; Use smaller code for copying literals? + ; + ; This saves 11 bytes of code at the cost of 30% speed. + ; + + !if LZSA_SMALL_SIZE { +LZSA_SHORT_LZ = 1 + } else { +LZSA_SHORT_LZ = 0 + } + + ; + ; Macro to increment the source pointer to the next page. + ; + ; This should call a subroutine to determine if a bank + ; has been crossed, and a new bank should be paged in. + ; + + !macro LZSA_INC_PAGE { + inc +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +DECOMPRESS_LZSA1 + LDY #$00 + +DECODE_TOKEN + JSR GETSRC ; read token byte: O|LLL|MMMM + PHA ; preserve token on stack + + AND #$70 ; isolate literals count + BEQ NO_LITERALS ; skip if no literals to copy + LSR ; shift literals count into place + LSR + LSR + LSR + CMP #$07 ; LITERALS_RUN_LEN? + BCC PREPARE_COPY_LITERALS ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$F9 ; (LITERALS_RUN_LEN) + BCC PREPARE_COPY_LITERALS + BEQ LARGE_VARLEN_LITERALS ; if adding up to zero, go grab 16-bit count + + JSR GETSRC ; get single extended byte of variable literals count + INY ; add 256 to literals count + BCS PREPARE_COPY_LITERALS ; (*like JMP PREPARE_COPY_LITERALS but shorter) + +LARGE_VARLEN_LITERALS ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + TXA + +PREPARE_COPY_LITERALS + TAX + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + BMI GET_LONG_OFFSET ; $80: 16 bit offset + + JSR GETSRC ; get 8 bit offset from stream in A + TAX ; save for later + LDA #$FF ; high 8 bits + BNE GOT_OFFSET ; go prepare match + ; (*like JMP GOT_OFFSET but shorter) + +SHORT_VARLEN_MATCHLEN + JSR GETSRC ; get single extended byte of variable match len + INY ; add 256 to match length + +PREPARE_COPY_MATCH + TAX +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + DEC COPY_MATCH_LOOP+2 +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + INC COPY_MATCH_LOOP+2 +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + BEQ DECODE_TOKEN ; (*like JMP DECODE_TOKEN but shorter) + +GET_LONG_OFFSET ; handle 16 bit offset: + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + +GOT_OFFSET + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + STA OFFSHI ; store high 8 bits of offset + STX OFFSLO + + SEC ; substract dest - match offset + LDA PUTDST+1 +OFFSLO = *+1 + SBC #$AA ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + STA OFFSHI ; store high 8 bits of offset + TXA + + CLC ; add dest + match offset + ADC PUTDST+1 ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$0F ; isolate match len (MMMM) + ADC #$02 ; plus carry which is always set by the high ADC + CMP #$12 ; MATCH_RUN_LEN? + BCC PREPARE_COPY_MATCH ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; add MATCH_RUN_LEN and MIN_MATCH_SIZE to match length + BCC PREPARE_COPY_MATCH + BNE SHORT_VARLEN_MATCHLEN + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + ; large match length with zero high byte? + BNE PREPARE_COPY_MATCH_Y ; if not, continue + +DECOMPRESSION_DONE + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BNE PUTDST_DONE + DEC PUTDST+2 +PUTDST_DONE + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BNE GETSRC_DONE + DEC GETSRC+2 +GETSRC_DONE + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BNE PUTDST_DONE + INC PUTDST+2 +PUTDST_DONE + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BNE GETSRC_DONE + INC GETSRC+2 +GETSRC_DONE + RTS + +} diff --git a/loader/tools/lzsa/asm/6502/decompress_small_v2.asm b/loader/tools/lzsa/asm/6502/decompress_small_v2.asm new file mode 100644 index 0000000..42a7520 --- /dev/null +++ b/loader/tools/lzsa/asm/6502/decompress_small_v2.asm @@ -0,0 +1,332 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +NIBCOUNT = $FC ; zero-page location for temp offset + +DECOMPRESS_LZSA2 + LDY #$00 + STY NIBCOUNT + +DECODE_TOKEN + JSR GETSRC ; read token byte: XYZ|LL|MMM + PHA ; preserve token on stack + + AND #$18 ; isolate literals count (LL) + BEQ NO_LITERALS ; skip if no literals to copy + LSR ; shift literals count into place + LSR + LSR + CMP #$03 ; LITERALS_RUN_LEN_V2? + BCC PREPARE_COPY_LITERALS ; if less, count is directly embedded in token + + JSR GETNIBBLE ; get extra literals length nibble + ; add nibble to len from token + ADC #$02 ; (LITERALS_RUN_LEN_V2) minus carry + CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + BCC PREPARE_COPY_LITERALS ; if less, literals count is complete + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; overflow? + +PREPARE_COPY_LITERALS + TAX + BCC PREPARE_COPY_LITERALS_HIGH ; if not, literals count is complete + + ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_LITERALS_HIGH + TXA + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + ASL + BCS REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset + + ASL ; 0YZ: 5 or 9 bit offset + BCS OFFSET_9_BIT + + ; 00Z: 5 bit offset + + LDX #$FF ; set offset bits 15-8 to 1 + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 0, read nibble for bits 4-1 + ORA #$E0 ; set bits 7-5 to 1 + BNE GOT_OFFSET_LO ; go store low byte of match offset and prepare match + +OFFSET_9_BIT ; 01Z: 9 bit offset + ROL ; carry: Z bit; A: xxxxxxx1 (carry known set from BCS OFFSET_9_BIT) + ADC #$00 ; if Z bit is set, add 1 to A (bit 0 of A is now 0), otherwise bit 0 is 1 + ORA #$FE ; set offset bits 15-9 to 1. reversed Z is already in bit 0 + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_LARGE_OFFSET + ASL ; 13 bit offset? + BCS REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 8, read nibble for bits 12-9 + ADC #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_16_BIT ; rep-match or 16 bit offset + BMI REP_MATCH ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + JSR GETSRC ; grab high 8 bits +GOT_OFFSET_HI + TAX + JSR GETSRC ; grab low 8 bits +GOT_OFFSET_LO + STA OFFSLO ; store low byte of match offset + STX OFFSHI ; store high byte of match offset + +REP_MATCH +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + SEC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + SBC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address + LDA PUTDST+2 +OFFSHI = *+1 + SBC #$AA ; high 8 bits + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + SEC + +} else { + + ; Forward decompression - add match offset + + CLC ; add dest + match offset + LDA PUTDST+1 ; low 8 bits +OFFSLO = *+1 + ADC #$AA + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + PLA ; retrieve token from stack again + AND #$07 ; isolate match len (MMM) + ADC #$01 ; add MIN_MATCH_SIZE_V2 and carry + CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + BCC PREPARE_COPY_MATCH ; if less, length is directly embedded in token + + JSR GETNIBBLE ; get extra match length nibble + ; add nibble to len from token + ADC #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + BCC PREPARE_COPY_MATCH ; if less, match length is complete + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$E8 ; overflow? + +PREPARE_COPY_MATCH + TAX + BCC PREPARE_COPY_MATCH_Y ; if not, the match length is complete + BEQ DECOMPRESSION_DONE ; if EOD code, bail + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + LDA COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + DEC COPY_MATCH_LOOP+2 +GETMATCH_DONE + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + BNE GETMATCH_DONE + INC COPY_MATCH_LOOP+2 +GETMATCH_DONE + +} + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + JMP DECODE_TOKEN + +GETCOMBINEDBITS + EOR #$80 + ASL + PHP + + JSR GETNIBBLE ; get nibble into bits 0-3 (for offset bits 1-4) + PLP ; merge Z bit as the carry bit (for offset bit 0) + ROL ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +DECOMPRESSION_DONE + RTS + +GETNIBBLE +NIBBLES = *+1 + LDA #$AA + LSR NIBCOUNT + BCS HAS_NIBBLES + + INC NIBCOUNT + JSR GETSRC ; get 2 nibbles + STA NIBBLES + LSR + LSR + LSR + LSR + SEC + +HAS_NIBBLES + AND #$0F ; isolate low 4 bits of nibble + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + LDA PUTDST+1 + BNE PUTDST_DONE + DEC PUTDST+2 +PUTDST_DONE + DEC PUTDST+1 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + PHA + LDA GETSRC+1 + BNE GETSRC_DONE + DEC GETSRC+2 +GETSRC_DONE + DEC GETSRC+1 + PLA + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + STA $AAAA + INC PUTDST+1 + BNE PUTDST_DONE + INC PUTDST+2 +PUTDST_DONE + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + LDA $AAAA + INC GETSRC+1 + BNE GETSRC_DONE + INC GETSRC+2 +GETSRC_DONE + RTS + +} + diff --git a/loader/tools/lzsa/asm/65816/decompress_v1.asm b/loader/tools/lzsa/asm/65816/decompress_v1.asm new file mode 100644 index 0000000..4754e55 --- /dev/null +++ b/loader/tools/lzsa/asm/65816/decompress_v1.asm @@ -0,0 +1,281 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA1 block. Create one with lzsa -r +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI/LZSA_SRC_BANK contain the compressed raw block address +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK contain the destination buffer address +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI/LZSA_SRC_BANK must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI/BANK contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019-2020 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +!cpu 65816 +DECOMPRESS_LZSA1 + SEP #$30 +!as +!rs + LDY #$00 + +DECODE_TOKEN + JSR GETSRC ; read token byte: O|LLL|MMMM + PHA ; preserve token on stack + + AND #$70 ; isolate literals count + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$70 ; LITERALS_RUN_LEN? + BNE PREPARE_COPY_LITERALS ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$F9 ; (LITERALS_RUN_LEN) + BCC PREPARE_COPY_LITERALS_DIRECT + BEQ LARGE_VARLEN_LITERALS ; if adding up to zero, go grab 16-bit count + + JSR GETSRC ; get single extended byte of variable literals count + INY ; add 256 to literals count + BCS PREPARE_COPY_LITERALS_DIRECT ; (*like JMP PREPARE_COPY_LITERALS_DIRECT but shorter) + +LARGE_VARLEN_LITERALS ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + TXA + BCS PREPARE_COPY_LARGE_LITERALS ; (*like JMP PREPARE_COPY_LITERALS_DIRECT but shorter) + +PREPARE_COPY_LITERALS + TAX + LDA SHIFT_TABLE-1,X ; shift literals length into place + ; -1 because position 00 is reserved +PREPARE_COPY_LITERALS_DIRECT + TAX + +PREPARE_COPY_LARGE_LITERALS + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + BMI GET_LONG_OFFSET ; $80: 16 bit offset + + JSR GETSRC ; get 8 bit offset from stream in A + TAX ; save for later + LDA #$FF ; high 8 bits + BNE GOT_OFFSET ; go prepare match + ; (*like JMP GOT_OFFSET but shorter) + +SHORT_VARLEN_MATCHLEN + JSR GETSRC ; get single extended byte of variable match len + INY ; add 256 to match length + +PREPARE_COPY_MATCH + TAX +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + + REP #$20 +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + +} + SEP #$20 + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + BEQ DECODE_TOKEN ; (*like JMP DECODE_TOKEN but shorter) + +GET_LONG_OFFSET ; handle 16 bit offset: + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + +GOT_OFFSET + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + STA OFFSHI ; store high 8 bits of offset + STX OFFSLO + + SEC ; substract dest - match offset + REP #$20 +!al + LDA PUTDST+1 +OFFSLO = *+1 +OFFSHI = *+2 + SBC #$AAAA ; 16 bits + STA COPY_MATCH_LOOP+1 ; store back reference address + SEP #$20 +!as + SEC + +} else { + + ; Forward decompression - add match offset + + STA OFFSHI ; store high 8 bits of offset + TXA + + CLC ; add dest + match offset + ADC PUTDST+1 ; low 8 bits + STA COPY_MATCH_LOOP+1 ; store back reference address +OFFSHI = *+1 + LDA #$AA ; high 8 bits + + ADC PUTDST+2 + STA COPY_MATCH_LOOP+2 ; store high 8 bits of address + +} + + LDA PUTDST+3 ; bank + STA COPY_MATCH_LOOP+3 ; store back reference address + + PLA ; retrieve token from stack again + AND #$0F ; isolate match len (MMMM) + ADC #$02 ; plus carry which is always set by the high ADC + CMP #$12 ; MATCH_RUN_LEN? + BCC PREPARE_COPY_MATCH ; if not, count is directly embedded in token + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; add MATCH_RUN_LEN and MIN_MATCH_SIZE to match length + BCC PREPARE_COPY_MATCH + BNE SHORT_VARLEN_MATCHLEN + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + ; large match length with zero high byte? + BNE PREPARE_COPY_MATCH_Y ; if not, continue + +DECOMPRESSION_DONE + RTS + +SHIFT_TABLE + !BYTE $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !BYTE $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 + !BYTE $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 + !BYTE $03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03 + !BYTE $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04 + !BYTE $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05 + !BYTE $06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06 + !BYTE $07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07 + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 +LZSA_DST_BANK = *+3 + STA $AAAAAA + REP #$20 + DEC PUTDST+1 + SEP #$20 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 +LZSA_SRC_BANK = *+3 + LDA $AAAAAA + REP #$20 + DEC GETSRC+1 + SEP #$20 + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 +LZSA_DST_BANK = *+3 + STA $AAAAAA + REP #$20 + INC PUTDST+1 + SEP #$20 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 +LZSA_SRC_BANK = *+3 + LDA $AAAAAA + REP #$20 + INC GETSRC+1 + SEP #$20 + RTS +} diff --git a/loader/tools/lzsa/asm/65816/decompress_v2.asm b/loader/tools/lzsa/asm/65816/decompress_v2.asm new file mode 100644 index 0000000..08c2ac8 --- /dev/null +++ b/loader/tools/lzsa/asm/65816/decompress_v2.asm @@ -0,0 +1,338 @@ +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI/LZSA_SRC_BANK contain the compressed raw block address +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK contain the destination buffer address +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI/LZSA_SRC_BANK must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI/LZSA_DST_BANK must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI/BANK contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019-2020 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +!cpu 65816 +NIBCOUNT = $FC ; zero-page location for temp offset + +DECOMPRESS_LZSA2 + SEP #$30 +!as +!rs + LDY #$00 + STY NIBCOUNT + +DECODE_TOKEN + JSR GETSRC ; read token byte: XYZ|LL|MMM + PHA ; preserve token on stack + + AND #$18 ; isolate literals count (LL) + BEQ NO_LITERALS ; skip if no literals to copy + CMP #$18 ; LITERALS_RUN_LEN_V2? + BCC PREPARE_COPY_LITERALS ; if less, count is directly embedded in token + + JSR GETNIBBLE ; get extra literals length nibble + ; add nibble to len from token + ADC #$02 ; (LITERALS_RUN_LEN_V2) minus carry + CMP #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + BCC PREPARE_COPY_LITERALS_DIRECT ; if less, literals count is complete + + JSR GETSRC ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$EE ; overflow? + BRA PREPARE_COPY_LITERALS_DIRECT + +PREPARE_COPY_LITERALS_LARGE + ; handle 16 bits literals count + ; literals count = directly these 16 bits + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + BCS PREPARE_COPY_LITERALS_HIGH ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +PREPARE_COPY_LITERALS + LSR ; shift literals count into place + LSR + LSR + +PREPARE_COPY_LITERALS_DIRECT + TAX + BCS PREPARE_COPY_LITERALS_LARGE ; if so, literals count is large + +PREPARE_COPY_LITERALS_HIGH + TXA + BEQ COPY_LITERALS + INY + +COPY_LITERALS + JSR GETPUT ; copy one byte of literals + DEX + BNE COPY_LITERALS + DEY + BNE COPY_LITERALS + +NO_LITERALS + PLA ; retrieve token from stack + PHA ; preserve token again + ASL + BCS REPMATCH_OR_LARGE_OFFSET ; 1YZ: rep-match or 13/16 bit offset + + ASL ; 0YZ: 5 or 9 bit offset + BCS OFFSET_9_BIT + + ; 00Z: 5 bit offset + + LDX #$FF ; set offset bits 15-8 to 1 + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 0, read nibble for bits 4-1 + ORA #$E0 ; set bits 7-5 to 1 + BNE GOT_OFFSET_LO ; go store low byte of match offset and prepare match + +OFFSET_9_BIT ; 01Z: 9 bit offset + ;;ASL ; shift Z (offset bit 8) in place + ROL + ROL + AND #$01 + EOR #$FF ; set offset bits 15-9 to 1 + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_LARGE_OFFSET + ASL ; 13 bit offset? + BCS REPMATCH_OR_16_BIT ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + JSR GETCOMBINEDBITS ; rotate Z bit into bit 8, read nibble for bits 12-9 + ADC #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + BNE GOT_OFFSET_HI ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +REPMATCH_OR_16_BIT ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + BMI REP_MATCH ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + JSR GETSRC ; grab high 8 bits +GOT_OFFSET_HI + TAX + JSR GETSRC ; grab low 8 bits +GOT_OFFSET_LO + STA OFFSLO ; store low byte of match offset + STX OFFSHI ; store high byte of match offset + +REP_MATCH +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression - substract match offset + + SEC ; add dest + match offset + REP #$20 +!al + LDA PUTDST+1 ; 16 bits +OFFSLO = *+1 +OFFSHI = *+2 + SBC #$AAAA + STA COPY_MATCH_LOOP+1 ; store back reference address + SEP #$20 +!as + SEC + +} else { + + ; Forward decompression - add match offset + + CLC ; add dest + match offset + REP #$20 +!al + LDA PUTDST+1 ; 16 bits +OFFSLO = *+1 +OFFSHI = *+2 + ADC #$AAAA + STA COPY_MATCH_LOOP+1 ; store back reference address + SEP #$20 +!as +} + + LDA PUTDST+3 ; bank + STA COPY_MATCH_LOOP+3 ; store back reference address + + PLA ; retrieve token from stack again + AND #$07 ; isolate match len (MMM) + ADC #$01 ; add MIN_MATCH_SIZE_V2 and carry + CMP #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + BCC PREPARE_COPY_MATCH ; if less, length is directly embedded in token + + JSR GETNIBBLE ; get extra match length nibble + ; add nibble to len from token + ADC #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + CMP #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + BCC PREPARE_COPY_MATCH ; if less, match length is complete + + JSR GETSRC ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + SBC #$E8 ; overflow? + +PREPARE_COPY_MATCH + TAX + BCC PREPARE_COPY_MATCH_Y ; if not, the match length is complete + BEQ DECOMPRESSION_DONE ; if EOD code, bail + + ; Handle 16 bits match length + JSR GETLARGESRC ; grab low 8 bits in X, high 8 bits in A + TAY ; put high 8 bits in Y + +PREPARE_COPY_MATCH_Y + TXA + BEQ COPY_MATCH_LOOP + INY + +COPY_MATCH_LOOP + LDA $AAAAAA ; get one byte of backreference + JSR PUTDST ; copy to destination + + REP #$20 +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- put backreference bytes backward + + DEC COPY_MATCH_LOOP+1 + +} else { + + ; Forward decompression -- put backreference bytes forward + + INC COPY_MATCH_LOOP+1 + +} + SEP #$20 + + DEX + BNE COPY_MATCH_LOOP + DEY + BNE COPY_MATCH_LOOP + JMP DECODE_TOKEN + +GETCOMBINEDBITS + EOR #$80 + ASL + PHP + + JSR GETNIBBLE ; get nibble into bits 0-3 (for offset bits 1-4) + PLP ; merge Z bit as the carry bit (for offset bit 0) +COMBINEDBITZ + ROL ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +DECOMPRESSION_DONE + RTS + +GETNIBBLE +NIBBLES = *+1 + LDA #$AA + LSR NIBCOUNT + BCC NEED_NIBBLES + AND #$0F ; isolate low 4 bits of nibble + RTS + +NEED_NIBBLES + INC NIBCOUNT + JSR GETSRC ; get 2 nibbles + STA NIBBLES + LSR + LSR + LSR + LSR + SEC + RTS + +!ifdef BACKWARD_DECOMPRESS { + + ; Backward decompression -- get and put bytes backward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 +LZSA_DST_BANK = *+3 + STA $AAAAAA + REP #$20 + DEC PUTDST+1 + SEP #$20 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 +LZSA_SRC_BANK = *+3 + LDA $AAAAAA + REP #$20 + DEC GETSRC+1 + SEP #$20 + RTS + +} else { + + ; Forward decompression -- get and put bytes forward + +GETPUT + JSR GETSRC +PUTDST +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 +LZSA_DST_BANK = *+3 + STA $AAAAAA + REP #$20 + INC PUTDST+1 + SEP #$20 + RTS + +GETLARGESRC + JSR GETSRC ; grab low 8 bits + TAX ; move to X + ; fall through grab high 8 bits + +GETSRC +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 +LZSA_SRC_BANK = *+3 + LDA $AAAAAA + REP #$20 + INC GETSRC+1 + SEP #$20 + RTS +} diff --git a/loader/tools/lzsa/asm/6809/unlzsa1-6309.s b/loader/tools/lzsa/asm/6809/unlzsa1-6309.s new file mode 100644 index 0000000..5866e8d --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa1-6309.s @@ -0,0 +1,90 @@ +; unlzsa1-6309.s - Hitachi 6309 decompression routine for raw LZSA1 - 92 bytes +; compress with lzsa -f1 -r +; +; in: x = start of compressed data +; y = start of decompression buffer +; out: y = end of decompression buffer + 1 +; +; Copyright (C) 2020 Emmanuel Marty, Doug Masten +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa1 equ lz1token + +lz1bigof lda ,x+ ; O set: load MSB 16-bit (negative, signed) offest +lz1gotof leau d,y ; put backreference start address in U (dst+offset) + + ldd #$000f ; clear MSB match length and set mask for MMMM + andb ,s+ ; isolate MMMM (embedded match length) in token + addb #$03 ; add MIN_MATCH_SIZE + cmpb #$12 ; MATCH_RUN_LEN? + bne lz1gotln ; no, we have the full match length, go copy + + addb ,x+ ; add extra match length byte + MIN_MATCH_SIZE + MATCH_RUN_LEN + bcc lz1gotln ; if no overflow, we have the full length + bne lz1midln + + ldb ,x+ ; load 16-bit len in D (low part in B, high in A) + lda ,x+ ; (little endian) + bne lz1gotln ; check if we hit EOD (16-bit length = 0) + tstb + bne lz1gotln ; go copy matched bytes if not + + rts ; done, bail + +lz1midln tfr b,a ; copy high part of len into A + ldb ,x+ ; grab low 8 bits of len in B + +lz1gotln tfr d,w ; set W with match length for TFM instruction + tfm u+,y+ ; copy match bytes + +lz1token ldb ,x+ ; load next token into B: O|LLL|MMMM + pshs b ; save it + + andb #$70 ; isolate LLL (embedded literals count) in B + beq lz1nolt ; skip if no literals + cmpb #$70 ; LITERALS_RUN_LEN? + bne lz1declt ; if not, we have the complete count, go unshift + + ldb ,x+ ; load extra literals count byte + addb #$07 ; add LITERALS_RUN_LEN + bcc lz1gotla ; if no overflow, we got the complete count, copy + bne lz1midlt + + ldb ,x+ ; load low 8 bits of little-endian literals count + lda ,x+ ; load high 8 bits of literal count + bra lz1gotlt ; we now have the complete count, go copy + +lz1midlt tfr b,a ; copy high part of literals count into A + ldb ,x+ ; load low 8 bits of literals count + bra lz1gotlt ; we now have the complete count, go copy + +lz1declt lsrb ; shift literals count into place + lsrb + lsrb + lsrb + +lz1gotla clra ; clear A (high part of literals count) +lz1gotlt tfr d,w ; set W with literals count for TFM instruction + tfm x+,y+ ; copy literal bytes + +lz1nolt ldb ,x+ ; load either 8-bit or LSB 16-bit offset (negative, signed) + lda ,s ; get token again, don't pop it from the stack + bmi lz1bigof ; test O bit (small or large offset) + + lda #$ff ; set high 8 bits + bra lz1gotof diff --git a/loader/tools/lzsa/asm/6809/unlzsa1.s b/loader/tools/lzsa/asm/6809/unlzsa1.s new file mode 100644 index 0000000..559a303 --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa1.s @@ -0,0 +1,102 @@ +; unlzsa1.s - 6809 decompression routine for raw LZSA1 - 110 bytes +; compress with lzsa -r +; +; in: x = start of compressed data +; y = start of decompression buffer +; out: y = end of decompression buffer + 1 +; +; Copyright (C) 2020 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa1 equ lz1token + +lz1bigof lda ,x+ ; O set: load MSB 16-bit (negative, signed) offest +lz1gotof leau d,y ; put backreference start address in U (dst+offset) + + ldd #$000f ; clear MSB match length and set mask for MMMM + andb ,s+ ; isolate MMMM (embedded match length) in token + addb #$03 ; add MIN_MATCH_SIZE + cmpb #$12 ; MATCH_RUN_LEN? + bne lz1gotln ; no, we have the full match length, go copy + + addb ,x+ ; add extra match length byte + MIN_MATCH_SIZE + MATCH_RUN_LEN + bcc lz1gotln ; if no overflow, we have the full length + bne lz1midln + + ldb ,x+ ; load 16-bit len in D (low part in B, high in A) + lda ,x+ ; (little endian) + bne lz1gotln ; check if we hit EOD (16-bit length = 0) + tstb + bne lz1gotln ; go copy matched bytes if not + + rts ; done, bail + +lz1midln tfr b,a ; copy high part of len into A + ldb ,x+ ; grab low 8 bits of len in B + +lz1gotln pshs x ; save source compressed data pointer + tfr d,x ; copy match length to X + +lz1cpymt lda ,u+ ; copy matched byte + sta ,y+ + leax -1,x ; decrement X + bne lz1cpymt ; loop until all matched bytes are copied + + puls x ; restore source compressed data pointer + +lz1token ldb ,x+ ; load next token into B: O|LLL|MMMM + pshs b ; save it + + andb #$70 ; isolate LLL (embedded literals count) in B + beq lz1nolt ; skip if no literals + cmpb #$70 ; LITERALS_RUN_LEN? + bne lz1declt ; if not, we have the complete count, go unshift + + ldb ,x+ ; load extra literals count byte + addb #$07 ; add LITERALS_RUN_LEN + bcc lz1gotla ; if no overflow, we got the complete count, copy + bne lz1midlt + + ldb ,x+ ; load low 8 bits of little-endian literals count + lda ,x+ ; load high 8 bits of literal count + bra lz1gotlt ; we now have the complete count, go copy + +lz1midlt tfr b,a ; copy high part of literals count into A + ldb ,x+ ; load low 8 bits of literals count + bra lz1gotlt ; we now have the complete count, go copy + +lz1declt lsrb ; shift literals count into place + lsrb + lsrb + lsrb +lz1gotla clra ; clear A (high part of literals count) + +lz1gotlt leau ,x + tfr d,x ; transfer 16-bit count into X +lz1cpylt lda ,u+ ; copy literal byte + sta ,y+ + leax -1,x ; decrement X and update Z flag + bne lz1cpylt ; loop until all literal bytes are copied + leax ,u + +lz1nolt ldb ,x+ ; load either 8-bit or LSB 16-bit offset (negative, signed) + lda ,s ; get token again, don't pop it from the stack + bmi lz1bigof ; test O bit (small or large offset) + + lda #$ff ; set high 8 bits + bra lz1gotof diff --git a/loader/tools/lzsa/asm/6809/unlzsa1b-6309.s b/loader/tools/lzsa/asm/6809/unlzsa1b-6309.s new file mode 100644 index 0000000..6078085 --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa1b-6309.s @@ -0,0 +1,92 @@ +; unlzsa1-6309.s - H6309 backward decompressor for raw LZSA1 - 97 bytes +; compress with lzsa -f1 -r -b +; +; in: x = last byte of compressed data +; y = last byte of decompression buffer +; out: y = first byte of decompressed data +; +; Copyright (C) 2020 Emmanuel Marty, Doug Masten +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa1 + leax 1,x + bra lz1token + +lz1bigof ldd ,--x ; O set: load long 16-bit (negative, signed) offest +lz1gotof negd ; reverse sign of offset in D + leau d,y ; put backreference start address in U (dst+offset) + + ldd #$000f ; clear MSB match length and set mask for MMMM + andb ,s+ ; isolate MMMM (embedded match length) in token + addb #$03 ; add MIN_MATCH_SIZE + cmpb #$12 ; MATCH_RUN_LEN? + bne lz1gotln ; no, we have the full match length, go copy + + addb ,-x ; add extra match length byte + MIN_MATCH_SIZE + MATCH_RUN_LEN + bcc lz1gotln ; if no overflow, we have the full length + bne lz1midln + + ldd ,--x ; load 16-bit len in D (low part in B, high in A) + bne lz1gotln ; check if we hit EOD (16-bit length = 0) + + leay 1,y ; adjust pointer to first byte of decompressed data + rts ; done, bail + +lz1midln tfr b,a ; copy high part of len into A + ldb ,-x ; grab low 8 bits of len in B + +lz1gotln tfr d,w ; set W with match length for TFM instruction + tfm u-,y- ; copy match bytes + +lz1token ldb ,-x ; load next token into B: O|LLL|MMMM + pshs b ; save it + + andb #$70 ; isolate LLL (embedded literals count) in B + beq lz1nolt ; skip if no literals + cmpb #$70 ; LITERALS_RUN_LEN? + bne lz1declt ; if not, we have the complete count, go unshift + + ldb ,-x ; load extra literals count byte + addb #$07 ; add LITERALS_RUN_LEN + bcc lz1gotla ; if no overflow, we got the complete count, copy + bne lz1midlt + + ldd ,--x ; load 16 bit count in D (low part in B, high in A) + bra lz1gotlt ; we now have the complete count, go copy + +lz1midlt tfr b,a ; copy high part of literals count into A + ldb ,-x ; load low 8 bits of literals count + bra lz1gotlt ; we now have the complete count, go copy + +lz1declt lsrb ; shift literals count into place + lsrb + lsrb + lsrb + +lz1gotla clra ; clear A (high part of literals count) +lz1gotlt tfr d,w ; set W with literals count for TFM instruction + leax -1,x ; tfm is post-decrement + tfm x-,y- ; copy literal bytes + leax 1,x + +lz1nolt ldb ,s ; get token again, don't pop it from the stack + bmi lz1bigof ; test O bit (small or large offset) + + ldb ,-x ; load either 8-bit or LSB 16-bit offset (negative, signed) + lda #$ff ; set high 8 bits + bra lz1gotof diff --git a/loader/tools/lzsa/asm/6809/unlzsa1b.s b/loader/tools/lzsa/asm/6809/unlzsa1b.s new file mode 100644 index 0000000..ada6dcc --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa1b.s @@ -0,0 +1,105 @@ +; unlzsa1b.s - 6809 backward decompression routine for raw LZSA1 - 113 bytes +; compress with lzsa -r -b +; +; in: x = last byte of compressed data +; y = last byte of decompression buffer +; out: y = first byte of decompressed data +; +; Copyright (C) 2020 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa1 + leax 1,x + leay 1,y + bra lz1token + +lz1bigof ldd ,--x ; O set: load long 16 bit (negative, signed) offset +lz1gotof nega ; reverse sign of offset in D + negb + sbca #0 + leau d,y ; put backreference start address in U (dst+offset) + + ldd #$000f ; clear MSB match length and set mask for MMMM + andb ,s+ ; isolate MMMM (embedded match length) in token + + addb #$03 ; add MIN_MATCH_SIZE + cmpb #$12 ; MATCH_RUN_LEN? + bne lz1gotln ; no, we have the full match length, go copy + + addb ,-x ; add extra match length byte + MIN_MATCH_SIZE + MATCH_RUN_LEN + bcc lz1gotln ; if no overflow, we have the full length + bne lz1midln + + ldd ,--x ; load 16-bit len in D (low part in B, high in A) + bne lz1gotln ; check if we hit EOD (16-bit length = 0) + + rts ; done, bail + +lz1midln tfr b,a ; copy high part of len into A + ldb ,-x ; grab low 8 bits of len in B + +lz1gotln pshs x ; save source compressed data pointer + tfr d,x ; copy match length to X + +lz1cpymt lda ,-u ; copy matched byte + sta ,-y + leax -1,x ; decrement X + bne lz1cpymt ; loop until all matched bytes are copied + + puls x ; restore source compressed data pointer + +lz1token ldb ,-x ; load next token into B: O|LLL|MMMM + pshs b ; save it + + andb #$70 ; isolate LLL (embedded literals count) in B + beq lz1nolt ; skip if no literals + cmpb #$70 ; LITERALS_RUN_LEN? + bne lz1declt ; if not, we have the complete count, go unshift + + ldb ,-x ; load extra literals count byte + addb #$07 ; add LITERALS_RUN_LEN + bcc lz1gotla ; if no overflow, we got the complete count, copy + bne lz1midlt + + ldd ,--x ; load 16 bit count in D (low part in B, high in A) + bra lz1gotlt ; we now have the complete count, go copy + +lz1midlt tfr b,a ; copy high part of literals count into A + ldb ,-x ; load low 8 bits of literals count + bra lz1gotlt ; we now have the complete count, go copy + +lz1declt lsrb ; shift literals count into place + lsrb + lsrb + lsrb + +lz1gotla clra ; clear A (high part of literals count) +lz1gotlt leau ,x + tfr d,x ; transfer 16-bit count into X +lz1cpylt lda ,-u ; copy literal byte + sta ,-y + leax -1,x ; decrement X and update Z flag + bne lz1cpylt ; loop until all literal bytes are copied + leax ,u + +lz1nolt ldb ,s ; get token again, don't pop it from the stack + bmi lz1bigof ; test O bit (small or large offset) + + ldb ,-x ; O clear: load 8 bit (negative, signed) offset + lda #$ff ; set high 8 bits + bra lz1gotof diff --git a/loader/tools/lzsa/asm/6809/unlzsa2-6309.s b/loader/tools/lzsa/asm/6809/unlzsa2-6309.s new file mode 100644 index 0000000..17970d8 --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa2-6309.s @@ -0,0 +1,129 @@ +; unlzsa2-6309.s - Hitachi 6309 decompression routine for raw LZSA2 - 150 bytes +; compress with lzsa -f2 -r +; +; in: x = start of compressed data +; y = start of decompression buffer +; out: y = end of decompression buffer + 1 +; +; Copyright (C) 2020 Emmanuel Marty, Doug Masten +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa2 + clr lz2nibct ; reset nibble available flag + bra lz2token + +lz2nibct fcb 0 ; nibble ready flag + +lz2replg lslb ; push token's Y flag bit into carry + bcs lz2rep16 ; if token's Y bit is set, rep or 16 bit offset + + sex ; push token's Z flag bit into reg A + bsr lz2nibl ; get offset nibble in B + lsla ; push token's Z flag bit into carry + rolb ; shift Z flag from carry into bit 0 of B + eorb #$e1 ; set bits 13-15 of offset, reverse bit 8 + tfr b,a ; copy bits 8-15 of offset into A + suba #$02 ; substract 512 from offset + ldb ,x+ ; load low 8 bits of (negative, signed) offset + bra lz2gotof + +lz2rep16 bmi lz2repof ; if token's Z flag bit is set, rep match + ldd ,x++ ; load high then low 8 bits of offset + +lz2gotof std lz2moff+2 ; store match offset + +lz2repof ldd #$0007 ; clear MSB match length and set mask for MMM + andb ,u ; isolate MMM (embedded match length) in token +lz2moff leau $aaaa,y ; put backreference start address in U (dst+offset) + addb #$02 ; add MIN_MATCH_SIZE_V2 + cmpb #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + bne lz2gotln ; no, we have the full match length, go copy + + bsr lz2nibl ; get offset nibble in B + addb #$09 ; add MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + cmpb #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + bne lz2gotln ; if not, we have the full match length, go copy + + addb ,x+ ; add extra length byte + MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15 + bcc lz2gotln ; if no overflow, we have the full length + beq lz2done ; detect EOD code + + ldb ,x+ ; load 16-bit len in D (low part in B, high in A) + lda ,x+ ; (little endian) + +lz2gotln tfr d,w ; set W with match count for TFM instruction + tfm u+,y+ ; copy match bytes + +lz2token tfr x,u ; save token address + ldb ,x+ ; load next token into B: XYZ|LL|MMM + andb #$18 ; isolate LL (embedded literals count) in B + beq lz2nolt ; skip if no literals + cmpb #$18 ; LITERALS_RUN_LEN_V2? + bne lz2declt ; if not, we have the complete count, go unshift + + bsr lz2nibl ; get extra literals length nibble in B + addb #$03 ; add LITERALS_RUN_LEN_V2 + cmpb #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + bne lz2gotla ; if not, we have the full literals count, go copy + + addb ,x+ ; add extra literals count byte + LITERALS_RUN_LEN + 15 + bcc lz2gotla ; if no overflow, we got the complete count, copy + + ldb ,x+ ; load low 8 bits of little-endian literals count + lda ,x+ ; load high 8 bits of literal count + bra lz2gotlt ; we now have the complete count, go copy + +lz2declt lsrb ; shift literals count into place + lsrb + lsrb +lz2gotla clra ; clear A (high part of literals count) + +lz2gotlt tfr d,w ; set W with literals count for TFM instruction + tfm x+,y+ ; copy literal bytes + +lz2nolt ldb ,u ; get token again + lslb ; push token's X flag bit into carry + bcs lz2replg ; if token's X bit is set, rep or large offset + + lslb ; push token's Y flag bit into carry + sex ; push token's Z flag bit into reg A (carry flag is not effected) + bcs lz2offs9 ; if token's Y bit is set, 9 bits offset + + bsr lz2nibl ; get offset nibble in B + lsla ; retrieve token's Z flag bit and push into carry + rolb ; shift Z flag from carry into bit 0 of B + eorb #$e1 ; set bits 5-7 of offset, reverse bit 0 + sex ; set bits 8-15 of offset to $FF + bra lz2gotof + +lz2offs9 deca ; set bits 9-15 of offset, reverse bit 8 + ldb ,x+ ; load low 8 bits of (negative, signed) offset + bra lz2gotof + +lz2nibl ldb #$aa + com lz2nibct ; nibble ready? + bpl lz2gotnb + + ldb ,x+ ; load two nibbles + stb lz2nibl+1 ; store nibble for next time (low 4 bits) + lsrb ; shift 4 high bits of nibble down + lsrb + lsrb + lsrb +lz2gotnb andb #$0f ; only keep low 4 bits +lz2done rts diff --git a/loader/tools/lzsa/asm/6809/unlzsa2.s b/loader/tools/lzsa/asm/6809/unlzsa2.s new file mode 100644 index 0000000..a620cad --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa2.s @@ -0,0 +1,146 @@ +; unlzsa2.s - 6809 decompression routine for raw LZSA2 - 169 bytes +; compress with lzsa -f2 -r +; +; in: x = start of compressed data +; y = start of decompression buffer +; out: y = end of decompression buffer + 1 +; +; Copyright (C) 2020 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa2 + clr +; +; in: x = last byte of compressed data +; y = last byte of decompression buffer +; out: y = first byte of decompressed data +; +; Copyright (C) 2020 Emmanuel Marty, Doug Masten +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa2 + clr lz2nibct ; reset nibble available flag + leax 1,x ; adjust compressed data pointer + bra lz2token + +lz2nibct fcb 0 ; nibble ready flag + +lz2replg lslb ; push token's Y flag bit into carry + bcs lz2rep16 ; if token's Y bit is set, rep or 16 bit offset + + sex ; push token's Z flag bit into reg A + bsr lz2nibl ; get offset nibble in B + lsla ; push token's Z flag bit into carry + rolb ; shift Z flag from carry into bit 0 of B + eorb #$e1 ; set bits 13-15 of offset, reverse bit 8 + tfr b,a ; copy bits 8-15 of offset into A + suba #$02 ; substract 512 from offset + bra lz2lowof + +lz2rep16 bmi lz2repof ; if token's Z flag bit is set, rep match + lda ,-x ; load high 8 bits of (negative, signed) offset +lz2lowof ldb ,-x ; load low 8 bits of offset + +lz2gotof negd ; reverse sign of offset in D + std lz2moff+2 ; store match offset + +lz2repof ldd #$0007 ; clear MSB match length and set mask for MMM + andb ,u ; isolate MMM (embedded match length) in token +lz2moff leau $aaaa,y ; put backreference start address in U (dst+offset) + addb #$02 ; add MIN_MATCH_SIZE_V2 + cmpb #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + bne lz2gotln ; no, we have the full match length, go copy + + bsr lz2nibl ; get offset nibble in B + addb #$09 ; add MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + cmpb #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + bne lz2gotln ; if not, we have the full match length, go copy + + addb ,-x ; add extra length byte + MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15 + bcc lz2gotln ; if no overflow, we have the full length + beq lz2done ; detect EOD code + + ldd ,--x ; load 16-bit len in D (low part in B, high in A) + +lz2gotln tfr d,w ; set W with match count for TFM instruction + tfm u-,y- ; copy match bytes + +lz2token ldb ,-x ; load next token into B: XYZ|LL|MMM + tfr x,u ; save token address + andb #$18 ; isolate LL (embedded literals count) in B + beq lz2nolt ; skip if no literals + cmpb #$18 ; LITERALS_RUN_LEN_V2? + bne lz2declt ; if not, we have the complete count, go unshift + + bsr lz2nibl ; get extra literals length nibble in B + addb #$03 ; add LITERALS_RUN_LEN_V2 + cmpb #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + bne lz2gotla ; if not, we have the full literals count, go copy + + addb ,-x ; add extra literals count byte + LITERALS_RUN_LEN + 15 + bcc lz2gotla ; if no overflow, we got the complete count, copy + + ldd ,--x ; load 16 bit count in D (low part in B, high in A) + bra lz2gotlt ; we now have the complete count, go copy + +lz2nibl com lz2nibct ; nibble ready? + bpl lz2gotnb + + ldb ,-x ; load two nibbles + stb lz2gotnb+1 ; store nibble for next time (low 4 bits) + lsrb ; shift 4 high bits of nibble down + lsrb + lsrb + lsrb + rts + +lz2declt lsrb ; shift literals count into place + lsrb + lsrb +lz2gotla clra ; clear A (high part of literals count) + +lz2gotlt tfr d,w ; set W with literals count for TFM instruction + leax -1,x ; tfm is post-decrement + tfm x-,y- ; copy literal bytes + leax 1,x + +lz2nolt ldb ,u ; get token again + lslb ; push token's X flag bit into carry + bcs lz2replg ; if token's X bit is set, rep or large offset + + lslb ; push token's Y flag bit into carry + sex ; push token's Z flag bit into reg A (carry flag is not effected) + bcs lz2offs9 ; if token's Y bit is set, 9 bits offset + + bsr lz2nibl ; get offset nibble in B + lsla ; retrieve token's Z flag bit and push into carry + rolb ; shift Z flag from carry into bit 0 of B + eorb #$e1 ; set bits 5-7 of offset, reverse bit 0 + sex ; set bits 8-15 of offset to $FF + bra lz2gotof + +lz2offs9 deca ; set bits 9-15 of offset, reverse bit 8 + bra lz2lowof + +lz2done leay 1,y ; adjust pointer to first byte of decompressed data and then exit +lz2gotnb ldb #$aa ; load nibble + andb #$0f ; only keep low 4 bits + rts diff --git a/loader/tools/lzsa/asm/6809/unlzsa2b.s b/loader/tools/lzsa/asm/6809/unlzsa2b.s new file mode 100644 index 0000000..b538cac --- /dev/null +++ b/loader/tools/lzsa/asm/6809/unlzsa2b.s @@ -0,0 +1,152 @@ +; unlzsa2b.s - 6809 backward decompression routine for raw LZSA2 - 171 bytes +; compress with lzsa -f2 -r -b +; +; in: x = last byte of compressed data +; y = last byte of decompression buffer +; out: y = first byte of decompressed data +; +; Copyright (C) 2020 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +decompress_lzsa2 + clr 255 shuttle 113247 alice 59572 robotron 371612 ..+ +; rep stosw for long runs shuttle 113247 alice 59572 robotron 371612 ... +; rep movsw for long cpys shuttle 113247 alice 59572 robotron 371035 ..+ +; xchg/dec ah -> mov ah,val shuttle 112575 alice 59272 robotron 369198 +++ +; force >12h len.to longcpy shuttle 101998 alice 59266 robotron 364459 +.+ +; more efficient run branch shuttle 102239 alice 59297 robotron 364716 --- rb +; even more eff. run branch shuttle 101998 alice 59266 robotron 364459 *** +; BUGFIX - bad sign compare shuttle 101955 alice 59225 robotron 364117 +++ +; reverse 16-bit len compar shuttle 102000 alice 59263 robotron 364460 --- rb +; jcxz for EOD detection no change to speed, but is 1 byte shorter +++ +; force movsw for literals shuttle 107183 alice 62555 robotron 379524 --- rb +; defer shr4 until necessry shuttle 102069 alice 60236 robotron 364096 --- +; skip literals if LLL=0 shuttle 98655 alice 57849 robotron 363358 --- +; fall through to mid_liter shuttle 98595 alice 57789 robotron 361998 +++ +; == jumptable experiments begin == +; jumptable for small copys shuttle 101594 alice 61078 robotron 386018 --- +; start:xchg instead of mov shuttle 100948 alice 60467 robotron 381112 +++ +; use table for LLL=0 check shuttle 106972 alice 63333 robotron 388304 --- rb +; jmptbl to fallthrough mov shuttle 102532 alice 60760 robotron 383070 --- +; cpy fallthrough check_ofs shuttle 98939 alice 58917 robotron 371019 +** +; single jumptable jump shuttle 97528 alice 57264 robotron 362194 ++* +; conditional check for L=7 shuttle 98610 alice 58521 robotron 368153 --- rb +; rip out the jumptable :-/ shuttle 97616 alice 57128 robotron 360697 +++ +; defer add MIN_MATCH_SIZE shuttle 97250 alice 57004 robotron 361191 ++? +; cache constants in regs shuttle 104681 alice 59939 robotron 380125 --- rb diff --git a/loader/tools/lzsa/asm/8088/LZSA1JMP.ASM b/loader/tools/lzsa/asm/8088/LZSA1JMP.ASM new file mode 100644 index 0000000..a1eac9f --- /dev/null +++ b/loader/tools/lzsa/asm/8088/LZSA1JMP.ASM @@ -0,0 +1,581 @@ +; lzsa2fta.asm time-efficient decompressor implementation for 808x CPUs. +; Turbo Assembler IDEAL mode dialect. +; (Is supposed to also assemble with NASM's IDEAL mode support, but YMMV.) +; +; This code assembles to about 3K of lookup tables and unrolled code, +; but the tradeoff for that size is the absolute fastest decompressor +; of LZSA1 block data for 808x CPUs. +; If you need moderately fast code with less size, see LZSA1FTA.ASM. +; If you need the smallest decompression code, see decompress_small_v1.S. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; Additional speed optimizations by Pavel Zagrebin +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; +; =========================================================================== +; +; The key area to concentrate on when optimizing LZSA1 decompression speed is +; reducing time spent handling the shortest matches. This is for two reasons: +; 1. shorter matches are more common +; 2. short matches are least efficient in terms of decomp speed per byte +; You can confirm #1 using the --stats mode of the compressor. +; +; Branches are costly on 8086. To ensure we branch as little as possible, a +; jumptable will be used to branch directly to as many direct decode paths as +; possible. This will burn up 512 bytes of RAM for a jumptable, and a few +; hundred bytes of duplicated program code (rather than JMP/CALL common code +; blocks, we inline them to avoid the branch overhead). +; +; =========================================================================== +; +; === LZSA1 block reference: +; +; Blocks encoded as LZSA1 are composed from consecutive commands. +; Each command follows this format: +; +; token: +; optional extra literal length +; literal values +; match offset low +; optional match offset high +; optional extra encoded match length +; +; +; === LZSA1 Token Reference: +; +; 7 6 5 4 3 2 1 0 +; O L L L M M M M +; +; L: 3-bit literals length (0-6, or 7 if extended). If the number of literals for +; this command is 0 to 6, the length is encoded in the token and no extra bytes +; are required. Otherwise, a value of 7 is encoded and extra bytes follow as +; 'optional extra literal length' +; +; M: 4-bit encoded match length (0-14, or 15 if extended). Likewise, if the +; encoded match length for this command is 0 to 14, it is directly stored, +; otherwise 15 is stored and extra bytes follow as 'optional extra encoded match +; length'. Except for the last command in a block, a command always contains a +; match, so the encoded match length is the actual match length, offset by the +; minimum which is 3 bytes. For instance, an actual match length of 10 bytes to +; be copied, is encoded as 7. +; +; O: set for a 2-bytes match offset, clear for a 1-byte match offset +; +; +; === Decoding extended literal length: +; +; If the literals length is 7 or more, then an extra byte follows here, with +; three possible values: +; +; 0-248: the value is added to the 7 stored in the token. +; 250: a second byte follows. The final literals value is 256 + the second byte. +; 249: a little-endian 16-bit value follows, forming the final literals value. +; +; +; === Decoding match offsets: +; +; match offset low: The low 8 bits of the match offset follows. +; +; optional match offset high: If the 'O' bit (bit 7) is set in the token, the +; high 8 bits of the match offset follow, otherwise they are understood to be all +; set to 1. For instance, a short offset of 0x70 is interpreted as 0xff70 +; +; +; === Decoding extra encoded match length: +; +; optional extra encoded match length: If the encoded match length is 15 or more, +; the 'M' bits in the token form the value 15, and an extra byte follows here, +; with three possible types of value. +; +; 0-237: the value is added to the 15 stored in the token. +; The final value is 3 + 15 + this byte. +; 239: a second byte follows. The final match length is 256 + the second byte. +; 238: a second and third byte follow, forming a little-endian 16-bit value. +; The final encoded match length is that 16-bit value. +; +; =========================================================================== + + IDEAL ; Use Turbo Assembler IDEAL syntax checking + P8086 ; Restrict code generation to the 808x and later + JUMPS ; Perform fixups for out-of-bound conditional jumps + ; This is required for the (L=07 & M=0Fh) decode paths as they + ; have the most code, but these are uncommon paths so the + ; tiny speed loss in just these paths is not a concern. + +;Setting OPTIMIZE_LONG_RLE to 1 speeds up decompressing long runs of the +;same 16-bit word value, but hurts decompression speed of other data +;types slightly. Turn this on if you know your data has very long 16-bit +;word-based runs (reported as RLE2 sequences in the LZSA compressor output +;with an average length of at least 32 bytes), otherwise leave it off. + +OPTIMIZE_LONG_RLE EQU 0 + +SEGMENT CODE para public + +ASSUME cs:CODE, ds:CODE + +PUBLIC lzsa1_decompress_speed_jumptable + +; EQU helper statements (so we can construct a jump table without going crazy) + +minmatch EQU 3 +litrunlen EQU 7 + +leml1 EQU OFFSET lit_ext_mat_len_1b +leme1 EQU OFFSET lit_ext_mat_ext_1b +leml2 EQU OFFSET lit_ext_mat_len_2b +leme2 EQU OFFSET lit_ext_mat_ext_2b + +;short-circuit special cases for 0 through 6 literal copies: +l6ml1 EQU OFFSET lit_len_mat_len_1b_6 +l6me1 EQU OFFSET lit_len_mat_ext_1b +l6ml2 EQU OFFSET lit_len_mat_len_2b_6 +l6me2 EQU OFFSET lit_len_mat_ext_2b +l5ml1 EQU OFFSET lit_len_mat_len_1b_45 +l5me1 EQU OFFSET lit_len_mat_ext_1b + 1 +l5ml2 EQU OFFSET lit_len_mat_len_2b_45 +l5me2 EQU OFFSET lit_len_mat_ext_2b + 1 +l4ml1 EQU OFFSET lit_len_mat_len_1b_45 + 1 +l4me1 EQU OFFSET lit_len_mat_ext_1b + 2 +l4ml2 EQU OFFSET lit_len_mat_len_2b_45 + 1 +l4me2 EQU OFFSET lit_len_mat_ext_2b + 2 +l3ml1 EQU OFFSET lit_len_mat_len_1b_23 +l3me1 EQU OFFSET lit_len_mat_ext_1b + 3 +l3ml2 EQU OFFSET lit_len_mat_len_2b_23 +l3me2 EQU OFFSET lit_len_mat_ext_2b + 3 +l2ml1 EQU OFFSET lit_len_mat_len_1b_23 + 1 +l2me1 EQU OFFSET lit_len_mat_ext_1b + 4 +l2ml2 EQU OFFSET lit_len_mat_len_2b_23 + 1 +l2me2 EQU OFFSET lit_len_mat_ext_2b + 4 +l1ml1 EQU OFFSET lit_len_mat_len_1b_01 +l1me1 EQU OFFSET lit_len_mat_ext_1b + 5 +l1ml2 EQU OFFSET lit_len_mat_len_2b_01 +l1me2 EQU OFFSET lit_len_mat_ext_2b + 5 +l0ml1 EQU OFFSET lit_len_mat_len_1b_01 + 1 ; MMMM handling comes after LLL code +l0me1 EQU OFFSET lit_len_mat_ext_1b + 6 ; MMMM handling comes after LLL code +l0ml2 EQU OFFSET lit_len_mat_len_2b_01 + 1 ; MMMM handling comes after LLL code +l0me2 EQU OFFSET lit_len_mat_ext_2b + 6 ; MMMM handling comes after LLL code + +; 0 1 2 3 4 5 6 7 8 9 a b c d e f +jtbl DW l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0ml1,l0me1 ;0 + DW l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1ml1,l1me1 ;1 + DW l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2ml1,l2me1 ;2 + DW l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3ml1,l3me1 ;3 + DW l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4ml1,l4me1 ;4 + DW l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5ml1,l5me1 ;5 + DW l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6ml1,l6me1 ;6 + DW leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leml1,leme1 ;7 + DW l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0ml2,l0me2 ;8 + DW l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1ml2,l1me2 ;9 + DW l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2ml2,l2me2 ;a + DW l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3ml2,l3me2 ;b + DW l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4ml2,l4me2 ;c + DW l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5ml2,l5me2 ;d + DW l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6ml2,l6me2 ;e + DW leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leml2,leme2 ;f + +PROC lzsa1_decompress_speed_jumptable NEAR +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +MACRO get_byte_match_offset + mov ah,0ffh ;O=0, so set up offset's high byte + lodsb ;load low byte; ax=match offset + xchg bp,ax ;bp=match offset ax=00 + original token +ENDM + +MACRO get_word_match_offset + lodsw ;ax=match offset + xchg bp,ax ;bp=match offset ax=00 + original token +ENDM + +MACRO do_match_copy_long +LOCAL even0,even1,even2,do_run,do_run_w +; Copies a long match as optimally as possible. +; requirements: cx=length, bp=negative offset, ds:si=compdata, es:di=output +; trashes: ax, bx +; must leave cx=0 at exit + mov bx,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + lea si,[bp+di] ;si = output buffer + negative match offset + cmp bp,-2 ;do we have a byte/word run to optimize? +IF OPTIMIZE_LONG_RLE + jae do_run ;catch offset = -2 or -1 +ELSE + ja do_run ;catch offset = -1 +ENDIF + +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in @@do_run) So, let's copy faster with REP MOVSW. +;This affects 8088 only slightly, but is a bigger win on 8086 and higher. + shr cx,1 + jnc even0 + movsb +even0: + rep movsw + xchg si,ax ;restore si + mov ds,bx ;restore ds + jmp decode_token +do_run: +IF OPTIMIZE_LONG_RLE + je do_run_w ;if applicable, handle word-sized value faster +ENDIF + xchg dx,ax ;save si into dx, as ax is getting trashed + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + jnc even1 + stosb +even1: + rep stosw ;perform word run + mov si,dx ;restore si + mov ds,bx ;restore ds + jmp decode_token + +IF OPTIMIZE_LONG_RLE +do_run_w: + xchg dx,ax ;save si into dx, as ax is getting trashed + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + jnc even2 + stosb ;should be after rep stosw! +even2: + mov si,dx ;restore si + mov ds,bx ;restore ds + jmp decode_token +ENDIF +ENDM + +MACRO do_match_copy +; Copies a shorter match with as little overhead as possible. +; requirements: cx=length, bp=negative offset, ds:si=compdata, es:di=output +; trashes: ax, bx +; must leave cx=0 at exit + mov bx,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + lea si,[bp+di] ;si = output buffer + negative match offset + movsb + movsb + movsb ;Handle MINMATCH (instead of add cx,MINMATCH) + rep movsb + xchg si,ax ;restore si + mov ds,bx ;restore ds + jmp decode_token +ENDM + +MACRO do_literal_copy +LOCAL even +; Copies a literal sequence using words. +; Meant for longer lengths; for 128 bytes or less, use REP MOVSB. +; requirements: cx=length, ds:si=compdata, es:di=output +; must leave cx=0 at exit + shr cx,1 + jnc even + movsb +even: + rep movsw +ENDM + +MACRO copy_small_match_len + and al,0FH ;isolate length in token (MMMM) + xchg cx,ax ;cx=match length + do_match_copy ;copy match with cx=length, bp=offset +ENDM + +MACRO copy_large_match_len +LOCAL val239,val238,EOD +; Handle MMMM=Fh +; Assumptions: ah=0 from get_????_match_offset's xchg + lodsb ;grab extra match length byte + add al,0Fh+minmatch ;add MATCH_RUN_LEN + MIN_MATCH_SIZE +; jz val238 ;if zf & cf, 238: get 16-bit match length + jc val239 ;if cf, 239: get extra match length byte + xchg cx,ax ;otherwise, we have our match length + do_match_copy_long ;copy match with cx=length, bp=offset +val239: + jz val238 + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_match_copy_long ;copy match with cx=length, bp=offset +val238: + lodsw ;grab 16-bit length + xchg cx,ax + jcxz EOD ;is it the EOD marker? Exit if so + do_match_copy_long ;copy match with cx=length, bp=offset +EOD: + jmp done_decompressing +ENDM + + +lzsa1_start: + push di ;remember decompression offset + cld ;ensure string ops move forward + xor cx,cx + +decode_token: + xchg cx,ax ;clear ah (cx = 0 from match copy's REP) + lodsb ;read token byte: O|LLL|MMMM + mov bp,ax ;preserve 0+token in bp for later MMMM handling + mov bx,ax ;prep for table lookup + shl bx,1 ;adjust for offset word size + jmp [cs:jtbl+bx] ;jump directly to relevant decode path + +; There are eight basic decode paths for an LZSA1 token. Each of these +; paths perform only the necessary actions to decode the token and then +; fetch the next token. This results in a lot of code duplication, but +; it is the only way to get down to two branches per token (jump to unique +; decode path, then jump back to next token) for the most common cases. + +; Path #1: LLL=0-6, MMMM=0-Eh, O=0 (1-byte match offset) +; Handle LLL=0-6 by jumping directly into # of bytes to copy (6 down to 1) +lit_len_mat_len_1b_01: + movsb + get_byte_match_offset + copy_small_match_len +lit_len_mat_len_1b_23: + movsb + movsw + get_byte_match_offset + copy_small_match_len +lit_len_mat_len_1b_45: + movsb + movsw + movsw + get_byte_match_offset + copy_small_match_len +lit_len_mat_len_1b_6: + movsw + movsw + movsw + get_byte_match_offset + copy_small_match_len + +; Path #2: LLL=0-6, MMMM=Fh, O=0 (1-byte match offset) +lit_len_mat_ext_1b: + movsb + movsb + movsb + movsb + movsb + movsb + get_byte_match_offset + copy_large_match_len + + +; Path #3: LLL=7, MMMM=0-Eh, O=0 (1-byte match offset) +lit_ext_mat_len_1b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length +; jz @@val249_3 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_3 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_byte_match_offset + copy_small_match_len +@@val250_3: +jz @@val249_3 + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_small_match_len +@@val249_3: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_small_match_len + + +; Path #4: LLL=7, MMMM=Fh, O=0 (1-byte match offset) +lit_ext_mat_ext_1b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length +; jz @@val249_4 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_4 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_byte_match_offset + copy_large_match_len +@@val250_4: +jz @@val249_4 + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_large_match_len +@@val249_4: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_byte_match_offset + copy_large_match_len + + +; Path #5: LLL=0-6, MMMM=0-Eh, O=1 (2-byte match offset) +; Handle LLL=0-6 by jumping directly into # of bytes to copy (6 down to 1) +lit_len_mat_len_2b_01: + movsb + get_word_match_offset + copy_small_match_len +lit_len_mat_len_2b_23: + movsb + movsw + get_word_match_offset + copy_small_match_len +lit_len_mat_len_2b_45: + movsb + movsw + movsw + get_word_match_offset + copy_small_match_len +lit_len_mat_len_2b_6: + movsw + movsw + movsw + get_word_match_offset + copy_small_match_len + + +; Path #6: LLL=0-6, MMMM=Fh, O=1 (2-byte match offset) +; Path #6: LLL=0-6, MMMM=Fh, O=1 (2-byte match offset) +lit_len_mat_ext_2b: + movsb + movsb + movsb + movsb + movsb + movsb + get_word_match_offset + copy_large_match_len + +; Path #7: LLL=7, MMMM=0-Eh, O=1 (2-byte match offset) +lit_ext_mat_len_2b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length +; jz @@val249_7 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_7 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_word_match_offset + copy_small_match_len +@@val250_7: +jz @@val249_7 + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_small_match_len +@@val249_7: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_small_match_len + + +; Path #8: LLL=7, MMMM=Fh, O=1 (2-byte match offset) +lit_ext_mat_ext_2b: +; on entry: ax=0 + token, bp=ax + lodsb ;grab extra literal length byte + add al,litrunlen ;add 7h literal run length +; jz @@val249_8 ;if zf & cf, 249: get 16-bit literal length + jc @@val250_8 ;if cf, 250: get extra literal length byte + xchg cx,ax ;otherwise, we have our literal length + do_literal_copy ;this might be better as rep movsw !!! benchmark + get_word_match_offset + copy_large_match_len +@@val250_8: +jz @@val249_8 + lodsb ;ah=0; grab single extra length byte + inc ah ;ax=256+length byte + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_large_match_len +@@val249_8: + lodsw ;grab 16-bit length + xchg cx,ax + do_literal_copy + get_word_match_offset + copy_large_match_len + + +done_decompressing: +;return # of decompressed bytes in ax + pop ax ;retrieve the original decompression offset + sub di,ax ;adjust for original offset + xchg di,ax ;return adjusted value in ax + ret ;done decompressing, exit to caller + +ENDP lzsa1_decompress_speed_jumptable + +ENDS CODE + +END + + + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +; defer add MIN_MATCH_SIZE shuttle 97207 alice 57200 robotron 362884 ++* +; jumptable rewrite, no RLE shuttle 97744 alice 46905 robotron 309032 -++ +; adc cx,0 -> adc cl,0 shuttle 97744 alice 46893 robotron 309032 .+.! +; jumptable rewrite w/RLE shuttle 88776 alice 50433 robotron 319222 +-- +; short match copies movsb shuttle 97298 alice 49769 robotron 326282 ---rb +; long match copy #1 16-bit shuttle 92490 alice 46905 robotron 308722 +*+ +; long match copy #2 extraB shuttle 92464 alice 46905 robotron 308371 +.+ +; long match copy #3 0f->ed shuttle 86765 alice 46864 robotron 303895 +++! +; baseline new test harness shuttle 83925 alice 37948 robotron 269002 *** +; Pavel optimizations shuttle 82225 alice 36798 robotron 261226 +++ +; OPTIMIZE_LONG_RLE 1 shuttle 82242 alice 36787 robotron 261392 **- +; +;------ +; +;Pavel's optimization history: +; shuttle alice robotron time in 1.193 MHz timer clocks +;baseline 19109 D9A6 570F6 +;adc cl,0->adc cl,cl 19035 D9A6 56FAB +;rep movsb->shr cx,1;jnc 18FD4 D998 56F14 +;cmp bp,-2->inc bp;inc bp 18F07 D999 56EA3 +;jz;jc->jc 18D81 D973 56B2F +;add al,3->movsb x3 18B1E D777 56197 +;more lit_len_mat tables 18A83 D341 54ACC diff --git a/loader/tools/lzsa/asm/8088/LZSA2FTA.ASM b/loader/tools/lzsa/asm/8088/LZSA2FTA.ASM new file mode 100644 index 0000000..bf38e18 --- /dev/null +++ b/loader/tools/lzsa/asm/8088/LZSA2FTA.ASM @@ -0,0 +1,302 @@ +; lzsa2fta.asm - LZSA v2 time-efficient decompressor implementation for 8088 +; Turbo Assembler IDEAL mode dialect; can also be assembled with NASM. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + IDEAL + P8086 + MODEL SMALL + + CODESEG + +;While LZSA2 is technically capable of generating a match offset of -2, +;this sequence never actually showed up in my LZSA2 test corpus, likely due +;to compressor optimizations and the LZSA2 format itself. If you know your +;test data will contain a match offset of -2, you can enable code to write +;out the sequence very quickly at the cost of 18 bytes of code. +HANDLE_WORD_RUN EQU 0 + +PUBLIC lzsa2_decompress_speed + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +PROC lzsa2_decompress_speed NEAR + +MACRO get_nybble +LOCAL has_nybble + neg bh ;nybble ready? + jns has_nybble + xchg bx,ax + lodsb ;load two nybbles + xchg bx,ax +has_nybble: + mov cl,4 ;swap 4 high and low bits of nybble + ror bl,cl + mov cl,0FH + and cl,bl +ENDM + +lzsa2_speed_start: + push di ;remember decompression offset + cld ;make string operations go forward + xor cx,cx + mov bx,0100H ;bx used by get_nybble + +@@decode_token: + mov ax,cx ;clear ah - cx is zero (and must stay that way) + lodsb ;read token byte: XYZ|LL|MMMM + mov dx,ax ;keep copy of token in dl + + and al,018H ;isolate literals length in token (LL) + jz @@check_offset ;no literals? stop decoding, go to matches + +;At this point, al can be in three (unshifted) states: 1, 2, or 3. +;3 = not done yet. + cmp al,(2 shl 3) ;LITERALS_RUN_LEN_V2? (original: cmp al,03h) + jb @@lit1b ;LZSA2 output 1-byte more often, so test first + je @@lit2b + + mov cl,3 + shr al,cl ;shift literals length into place + get_nybble ;cl := get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,012H ;LITERALS_RUN_LEN_V2 + 15 ? + jne @@got_literals ;if not, we have the full literals count + lodsb ;grab extra length byte + add al,012H ;overflow? + jnc @@got_literals_big ;if not, we have a big full literals count + lodsw ;grab 16-bit extra length + +;For larger counts, it pays to set up a faster copy +@@got_literals_big: + xchg cx,ax + shr cx,1 + rep movsw + adc cx,0 + rep movsb + jmp @@check_offset + +@@got_literals: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + jmp @@check_offset + +;LZSA2 likes to produce tiny literals of 1 or 2 bytes. Handle them here. +@@lit2b:movsb +@@lit1b:movsb + +@@check_offset: + test dl,dl ;check match offset mode in token (X bit) + js @@rep_match_or_large_offset + + cmp dl,040H ;check if this is a 5 or 9-bit offset (Y bit) + jnb @@offset_9_bit + + ;5 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + mov al,020H ;shift Z (offset bit 4) in place + and al,dl + shl al,1 + shl al,1 + get_nybble ;get nybble for offset bits 0-3 + or al,cl ;merge nybble + rol al,1 + xor al,0E1H ;set offset bits 7-5 to 1 + dec ah ;set offset bits 15-8 to 1 + jmp @@get_match_length + +@@rep_match_or_16_bit: + test dl,020H ;test bit Z (offset bit 8) + jne @@repeat_match ;rep-match + + ;16 bit offset: + lodsw ;Get 2-byte match offset + xchg ah,al + jmp @@get_match_length + +@@offset_9_bit: + ;9 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + lodsb ;get 8 bit offset from stream in A + dec ah ;set offset bits 15-8 to 1 + test dl,020H ;test bit Z (offset bit 8) + je @@get_match_length + dec ah ;clear bit 8 if Z bit is clear + jmp @@get_match_length + +@@rep_match_or_large_offset: + cmp dl,0c0H ;check if this is a 13-bit offset + ;or a 16-bit offset/rep match (Y bit) + jnb @@rep_match_or_16_bit + + ;13 bit offset: + mov ah,020H ;shift Z (offset bit 12) in place + and ah,dl + shl ah,1 + shl ah,1 + get_nybble ;get nybble for offset bits 8-11 + or ah,cl ;merge nybble + rol ah,1 + xor ah,0E1H ;set offset bits 15-13 to 1 + sub ah,2 ;substract 512 + lodsb ;load match offset bits 0-7 + +@@get_match_length: + mov bp,ax ;bp:=offset +@@repeat_match: + mov ax,dx ;ax: original token + and al,07H ;isolate match length in token (MMM) + add al,2 ;add MIN_MATCH_SIZE_V2 + + cmp al,09H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne @@got_matchlen ;no, we have full match length from token + + get_nybble ;get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,018H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne @@got_matchlen ;no, we have full match length from token + + lodsb ;grab extra length byte + add al,018H ;overflow? + jnc @@got_matchlen_big ;if not, we have entire (big) length + je @@done_decompressing ; detect EOD code + + lodsw ;grab 16-bit length + +;If we're here, we have a larger match copy and can optimize how we do that +@@got_matchlen_big: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si now points at back reference in output data + add si,bp +IF HANDLE_WORD_RUN + cmp bp,-2 ;do we have a byte/word run to optimize? + jae @@do_run ;perform a run +ELSE + cmp bp,-1 ;do we have a byte run to optimize? + je @@do_run_1 ;perform a byte run +ENDIF +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. +; +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in @@do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + xchg si,ax + mov ds,dx ;restore ds:si + jmp @@decode_token ;go decode another token + +;Smaller match copies handled here: +@@got_matchlen: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si = back reference in output data + add si,bp + rep movsb ;copy match + xchg si,ax + mov ds,dx ;restore ds:si + jmp @@decode_token ;go decode another token + +@@done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done + +IF HANDLE_WORD_RUN +@@do_run: + je @@do_run_2 ;fall through to byte (common) if not word run +ENDIF + +@@do_run_1: + push ax + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + pop si + mov ds,dx + jmp @@decode_token ;go decode another token + +IF HANDLE_WORD_RUN +@@do_run_2: + push ax + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + pop si + mov ds,dx + jmp @@decode_token ;go decode another token +ENDIF + +ENDP lzsa2_decompress_speed + +ENDS + +END + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +;Compression corpus:shuttle alice robotro rletest largetx linewar ...... .. +;Start of exercise 160828 113311 665900 238507 1053865 1004237 ****** +;add al,val -> al,cl 160813 113296 668721 237484 1053604 1003815 ++-+++ +;sub ah,2 -> dec dec 160907 113585 666744 237484 1056651 1005172 --+*-- rb +;mov ax,cx->xchgcxax 159741 112460 660594 237477 1046770 998323 ++++++ +;unroll get_nibble 152552 106327 621119 237345 982381 942373 ++++++ +;early exit if LL=0 147242 103842 615559 239318 946863 942932 +++-+- +;push/pop->mov/mov 145447 100832 604822 237288 927017 931366 ++++++ +;push/pop->mov/mov(2)143214 98817 592920 239298 908217 910955 +++-++ +;rep stos for -1, -2 143289 102812 617087 237164 942081 940688 ---+-- rb +;larger literal cpys 143214 98817 591940 238296 907237 909657 **++++ +;larger copys & runs 132440 98802 586551 178768 904129 896709 ++++++ :-) +;smaller lit. copies 131991 99131 583933 177760 901824 898308 +-+++- +;swap smal lit compa 131828 99022 585121 177757 901793 894054 ++-*++ +;compare before shif 130587 95970 569908 177753 889221 872461 +++*++ +;getmatchlength base 130587 95970 570634 177753 893536 871556 ...... === +; f->rep_match_or_16 xxxxxx xxxxx 569910 xxxxxx 889266 871435 ..+.++ +; f->rep_match_or_la 129966 94748 566169 xxxxxx 880870 867030 +++.++ +++ +; f->offset_9_bit 132126 95258 568869 xxxxxx 893169 870364 -++.-+ +;final fallthrough 129966 94748 566169 177753 880870 865023 ****** diff --git a/loader/tools/lzsa/asm/8088/decompress_small_v1.S b/loader/tools/lzsa/asm/8088/decompress_small_v1.S new file mode 100755 index 0000000..ddb9196 --- /dev/null +++ b/loader/tools/lzsa/asm/8088/decompress_small_v1.S @@ -0,0 +1,120 @@ +; decompress_small.S - space-efficient decompressor implementation for 8088 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +lzsa1_decompress: + push di ; remember decompression offset + cld ; make string operations (lods, movs, stos..) move forward + + xor cx,cx + +.decode_token: + mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match + lodsb ; read token byte: O|LLL|MMMM + mov dx,ax ; keep token in dl + + and al,070H ; isolate literals length in token (LLL) + mov cl,4 + shr al,cl ; shift literals length into place + + cmp al,07H ; LITERALS_RUN_LEN? + jne .got_literals ; no, we have the full literals count from the token, go copy + + lodsb ; grab extra length byte + add al,07H ; add LITERALS_RUN_LEN + jnc .got_literals ; if no overflow, we have the full literals count, go copy + jne .mid_literals + + lodsw ; grab 16-bit extra length + db 81H ; mask inc ah/lodsb + ; (*like jmp short .got_literals but faster) + +.mid_literals: + inc ah ; add 256 + lodsb ; grab single extra length byte + +.got_literals: + xchg cx,ax + rep movsb ; copy cx literals from ds:si to es:di + + test dl,dl ; check match offset size in token (O bit) + js .get_long_offset + + dec cx + xchg cx,ax ; ah to 0xff - cx was zero from the rep movsb above + lodsb + db 3CH ; mask lodsw + ; (*like jmp short .get_match_length but faster) + +.get_long_offset: + lodsw ; Get 2-byte match offset + +.get_match_length: + xchg dx,ax ; dx: match offset ax: original token + and al,0FH ; isolate match length in token (MMMM) + add al,3 ; add MIN_MATCH_SIZE + + cmp al,012H ; MATCH_RUN_LEN? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,012H ; add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .got_matchlen ; if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ; grab 16-bit length + test ax,ax ; bail if we hit EOD + je short .done_decompressing + + db 81H ; mask inc ah/lodsb + ; (*like jmp short .got_literals but faster) +.mid_matchlen: + inc ah ; add 256 + lodsb ; grab single extra length byte + +.got_matchlen: + xchg cx,ax ; copy match length into cx + push ds ; save ds:si (current pointer to compressed data) + xchg si,ax + push es + pop ds + mov si,di ; ds:si now points at back reference in output data + add si,dx + rep movsb ; copy match + xchg si,ax ; restore ds:si + pop ds + jmp short .decode_token ; go decode another token + +.done_decompressing: + pop ax ; retrieve the original decompression offset + xchg ax,di ; compute decompressed size + sub ax,di + ret ; done diff --git a/loader/tools/lzsa/asm/8088/decompress_small_v2.S b/loader/tools/lzsa/asm/8088/decompress_small_v2.S new file mode 100755 index 0000000..16a95df --- /dev/null +++ b/loader/tools/lzsa/asm/8088/decompress_small_v2.S @@ -0,0 +1,176 @@ +; decompress_small.S - space-efficient decompressor implementation for 8088 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +lzsa2_decompress: + push di ; remember decompression offset + cld ; make string operations (lods, movs, stos..) move forward + + xor cx,cx + mov bx,0100H + xor bp,bp + +.decode_token: + mov ax,cx ; clear ah - cx is zero from above or from after rep movsb in .copy_match + lodsb ; read token byte: XYZ|LL|MMMM + mov dx,ax ; keep token in dl + + and al,018H ; isolate literals length in token (LL) + mov cl,3 + shr al,cl ; shift literals length into place + + cmp al,03H ; LITERALS_RUN_LEN_V2? + jne .got_literals ; no, we have the full literals count from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al,cl ; add len from token to nibble + cmp al,012H ; LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ; if not, we have the full literals count, go copy + + lodsb ; grab extra length byte + add al,012H ; overflow? + jnc .got_literals ; if not, we have the full literals count, go copy + + lodsw ; grab 16-bit extra length + +.got_literals: + xchg cx,ax + rep movsb ; copy cx literals from ds:si to es:di + + test dl,0C0h ; check match offset mode in token (X bit) + js .rep_match_or_large_offset + + ;;cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit) + ; discovered via the test with bit 6 set + xchg cx,ax ; clear ah - cx is zero from the rep movsb above + jne .offset_9_bit + + ; 5 bit offset + cmp dl,020H ; test bit 5 + call .get_nibble_x + jmp short .dec_offset_top + +.offset_9_bit: ; 9 bit offset + lodsb ; get 8 bit offset from stream in A + dec ah ; set offset bits 15-8 to 1 + test dl,020H ; test bit Z (offset bit 8) + je .get_match_length +.dec_offset_top: + dec ah ; clear bit 8 if Z bit is clear + ; or set offset bits 15-8 to 1 + jmp short .get_match_length + +.rep_match_or_large_offset: + ;;cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit) + jpe .rep_match_or_16_bit + + ; 13 bit offset + + cmp dl,0A0H ; test bit 5 (knowing that bit 7 is also set) + xchg ah,al + call .get_nibble_x + sub al,2 ; substract 512 + jmp short .get_match_length_1 + +.rep_match_or_16_bit: + test dl,020H ; test bit Z (offset bit 8) + jne .repeat_match ; rep-match + + ; 16 bit offset + lodsb ; Get 2-byte match offset + +.get_match_length_1: + xchg ah,al + lodsb ; load match offset bits 0-7 + +.get_match_length: + xchg bp,ax ; bp: offset +.repeat_match: + xchg ax,dx ; ax: original token + and al,07H ; isolate match length in token (MMM) + add al,2 ; add MIN_MATCH_SIZE_V2 + + cmp al,09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al,cl ; add len from token to nibble + cmp al,018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,018H ; overflow? + jnc .got_matchlen ; if not, we have the entire length + je short .done_decompressing ; detect EOD code + + lodsw ; grab 16-bit length + +.got_matchlen: + xchg cx,ax ; copy match length into cx + push ds ; save ds:si (current pointer to compressed data) + xchg si,ax + push es + pop ds + lea si,[bp+di] ; ds:si now points at back reference in output data + rep movsb ; copy match + xchg si,ax ; restore ds:si + pop ds + jmp .decode_token ; go decode another token + +.done_decompressing: + pop ax ; retrieve the original decompression offset + xchg di,ax ; compute decompressed size + sub ax,di + ret ; done + +.get_nibble_x: + cmc ; carry set if bit 4 was set + rcr al,1 + call .get_nibble ; get nibble for offset bits 0-3 + or al,cl ; merge nibble + rol al,1 + xor al,0E1H ; set offset bits 7-5 to 1 + ret + +.get_nibble: + neg bh ; nibble ready? + jns .has_nibble + + xchg bx,ax + lodsb ; load two nibbles + xchg bx,ax + +.has_nibble: + mov cl,4 ; swap 4 high and low bits of nibble + ror bl,cl + mov cl,0FH + and cl,bl + ret diff --git a/loader/tools/lzsa/asm/8088/decompress_speed_v1.S b/loader/tools/lzsa/asm/8088/decompress_speed_v1.S new file mode 100644 index 0000000..142bd15 --- /dev/null +++ b/loader/tools/lzsa/asm/8088/decompress_speed_v1.S @@ -0,0 +1,236 @@ +; decompress_speed_v1.S - time-efficient decompressor implementation for 8088 +; NASM syntax. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * ds:si: raw LZSA1 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +; Must declare this in the code segment: +SHR4table: + DB 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h + DB 01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h,01h + DB 02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h,02h + DB 03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h,03h + DB 04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h,04h + DB 05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h,05h + DB 06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h,06h + DB 07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h,07h + +lzsa1_decompress_speed: + push di ;remember decompression offset + cld ;ensure string ops move forward + mov bx,SHR4table + xor cx,cx + +.decode_token: + xchg cx,ax ;clear ah (cx = 0 from match copy's rep movsb) + lodsb ;read token byte: O|LLL|MMMM + mov dx,ax ;copy our token to dl for later MMMM handling + + and al,070H ;isolate literals length in token (LLL) + jz .check_offset_size ;if LLL=0, we have no literals; goto match + cmp al,070H ;LITERALS_RUN_LEN? + jne .got_literals ;no, we have full count from token; go copy + + lodsb ;grab extra length byte + add al,07H ;add LITERALS_RUN_LEN + jnc .got_literals_exact ;if no overflow, we have full count + je .big_literals + +.mid_literals: + lodsb ;grab single extra length byte + inc ah ;add 256 + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw ;We don't need to account for overlap because + adc cx,0 ;source for literals isn't the output buffer. + rep movsb + jmp .check_offset_size + +.big_literals: + lodsw ;grab 16-bit extra length + xchg cx,ax ;with longer counts, we can save some time + shr cx,1 ;by doing a word copy instead of a byte copy. + rep movsw + adc cx,0 + rep movsb + jmp .check_offset_size + +.got_literals: + cs xlat ;shift literals length into place +.got_literals_exact: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + +.check_offset_size: + test dl,dl ;check match offset size in token (O bit) + js .get_long_offset ;load absolute 16-bit match offset + + mov ah,0ffh ;set up high byte + lodsb ;load low byte + +.get_match_length: + xchg dx,ax ;dx: match offset ax: original token + and al,0FH ;isolate match length in token (MMMM) + cmp al,0FH ;MATCH_RUN_LEN? + jne .got_matchlen_short ;no, we have the full match length from the token, go copy + + lodsb ;grab extra length byte + add al,012H ;add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .do_long_copy ;if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ;grab 16-bit length + xchg cx,ax ;get ready to do a long copy + jcxz .done_decompressing ;wait, is it the EOD marker? Exit if so + jmp .copy_len_preset ;otherwise, do the copy + +.get_long_offset: + lodsw ;Get 2-byte match offset + jmp .get_match_length + +.got_matchlen_short: + add al,3 ;add MIN_MATCH_SIZE + xchg cx,ax ;copy match length into cx + mov bp,ds ;save ds + mov ax,es + mov ds,ax ;ds=es + xchg ax,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + rep movsb ;copy match + xchg si,ax ;restore si + mov ds,bp ;restore ds + jmp .decode_token ;go decode another token + +.done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done decompressing, exit to caller + +;With a confirmed longer match length, we have an opportunity to optimize for +;the case where a single byte is repeated long enough that we can benefit +;from rep movsw to perform the run (instead of rep movsb). +.mid_matchlen: + lodsb ;grab single extra length byte + inc ah ;add 256 +.do_long_copy: + xchg cx,ax ;copy match length into cx +.copy_len_preset: + push ds ;save ds + mov bp,es + mov ds,bp ;ds=es + mov bp,si ;save si + mov si,di ;ds:si now points at back reference in output data + add si,dx + cmp dx,-2 ;do we have a byte/word run to optimize? + jae .do_run ;perform a run +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. + +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in .do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +.do_run: + je .do_run_2 ;fall through to byte (common) if not word run + +.do_run_1: + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +.do_run_2: + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + mov si,bp ;restore si + pop ds + jmp .decode_token ;go decode another token + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +; original E. Marty code shuttle 123208 alice 65660 robotron 407338 *** +; table for shr al,4 shuttle 120964 alice 63230 robotron 394733 +++ +; push/pop to mov/mov shuttle 118176 alice 61835 robotron 386762 +++ +; movsw for literalcpys shuttle 124102 alice 64908 robotron 400220 --- rb +; stosw for byte runs shuttle 118897 alice 65040 robotron 403518 --- rb +; better stosw for runs shuttle 117712 alice 65040 robotron 403343 +-- +; disable RLE by default shuttle 116924 alice 60783 robotron 381226 +++ +; optimize got_matchlen shuttle 115294 alice 59588 robotron 374330 +++ +; fall through to getML shuttle 113258 alice 59572 robotron 372004 +++ +; fall through to midLI shuttle 113258 alice 59572 robotron 375060 ..- rb +; fall through midMaLen shuttle 113247 alice 59572 robotron 372004 +.+ +; movsw for litlen > 255 shuttle 113247 alice 59572 robotron 371612 ..+ +; rep stosw for long runs shuttle 113247 alice 59572 robotron 371612 ... +; rep movsw for long cpys shuttle 113247 alice 59572 robotron 371035 ..+ +; xchg/dec ah -> mov ah,val shuttle 112575 alice 59272 robotron 369198 +++ +; force >12h len.to longcpy shuttle 101998 alice 59266 robotron 364459 +.+ +; more efficient run branch shuttle 102239 alice 59297 robotron 364716 --- rb +; even more eff. run branch shuttle 101998 alice 59266 robotron 364459 *** +; BUGFIX - bad sign compare shuttle 101955 alice 59225 robotron 364117 +++ +; reverse 16-bit len compar shuttle 102000 alice 59263 robotron 364460 --- rb +; jcxz for EOD detection no change to speed, but is 1 byte shorter +++ +; force movsw for literals shuttle 107183 alice 62555 robotron 379524 --- rb +; defer shr4 until necessry shuttle 102069 alice 60236 robotron 364096 --- +; skip literals if LLL=0 shuttle 98655 alice 57849 robotron 363358 --- +; fall through to mid_liter shuttle 98595 alice 57789 robotron 361998 +++ +; == jumptable experiments begin == +; jumptable for small copys shuttle 101594 alice 61078 robotron 386018 --- +; start:xchg instead of mov shuttle 100948 alice 60467 robotron 381112 +++ +; use table for LLL=0 check shuttle 106972 alice 63333 robotron 388304 --- rb +; jmptbl to fallthrough mov shuttle 102532 alice 60760 robotron 383070 --- +; cpy fallthrough check_ofs shuttle 98939 alice 58917 robotron 371019 +** +; single jumptable jump shuttle 97528 alice 57264 robotron 362194 ++* +; conditional check for L=7 shuttle 98610 alice 58521 robotron 368153 --- rb +; rip out the jumptable :-/ shuttle 97616 alice 57128 robotron 360697 +++ +; defer add MIN_MATCH_SIZE shuttle 97250 alice 57004 robotron 361191 ++? +; cache constants in regs shuttle 104681 alice 59939 robotron 380125 --- rb diff --git a/loader/tools/lzsa/asm/8088/decompress_speed_v2.S b/loader/tools/lzsa/asm/8088/decompress_speed_v2.S new file mode 100644 index 0000000..973ae01 --- /dev/null +++ b/loader/tools/lzsa/asm/8088/decompress_speed_v2.S @@ -0,0 +1,288 @@ +; decompress_speed_v2.S - LZSA v2 time-efficient decompressor implementation for 8088 +; NASM syntax. +; +; Usual DOS assembler SMALL model assumptions apply. This code: +; - Assumes it was invoked via NEAR call (change RET to RETF for FAR calls) +; - Is interrupt-safe +; - Is not re-entrant (do not decompress while already running decompression) +; - Trashes all data and segment registers +; +; Copyright (C) 2019 Jim Leonard, Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 16 + +;While LZSA2 is technically capable of generating a match offset of -2, +;this sequence never actually showed up in my LZSA2 test corpus, likely due +;to compressor optimizations and the LZSA2 format itself. If you know your +;test data will contain a match offset of -2, you can enable code to write +;out the sequence very quickly at the cost of 18 bytes of code. +HANDLE_WORD_RUN EQU 0 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * ds:si: raw LZSA2 block +; * es:di: output buffer +; output: +; * ax: decompressed size +; --------------------------------------------------------------------------- + +%macro get_nybble 0 + neg bh ;nybble ready? + jns %%has_nybble + xchg bx,ax + lodsb ;load two nybbles + xchg bx,ax +%%has_nybble: + mov cl,4 ;swap 4 high and low bits of nybble + ror bl,cl + mov cl,0FH + and cl,bl +%endmacro + +lzsa2_decompress_speed: + push di ;remember decompression offset + cld ;make string operations go forward + xor cx,cx + mov bx,0100H ;bx used by get_nybble + +.decode_token: + mov ax,cx ;clear ah - cx is zero (and must stay that way) + lodsb ;read token byte: XYZ|LL|MMMM + mov dx,ax ;keep copy of token in dl + + and al,018H ;isolate literals length in token (LL) + jz .check_offset ;no literals? stop decoding, go to matches + +;At this point, al can be in three (unshifted) states: 1, 2, or 3. +;3 = not done yet. + cmp al,(2 << 3) ;LITERALS_RUN_LEN_V2? (original: cmp al,03h) + jb .lit1b ;LZSA2 output 1-byte more often, so test first + je .lit2b + + mov cl,3 + shr al,cl ;shift literals length into place + get_nybble ;cl := get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,012H ;LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ;if not, we have the full literals count + lodsb ;grab extra length byte + add al,012H ;overflow? + jnc .got_literals_big ;if not, we have a big full literals count + lodsw ;grab 16-bit extra length + +;For larger counts, it pays to set up a faster copy +.got_literals_big: + xchg cx,ax + shr cx,1 + rep movsw + adc cx,0 + rep movsb + jmp .check_offset + +.got_literals: + xchg cx,ax + rep movsb ;copy cx literals from ds:si to es:di + jmp .check_offset + +;LZSA2 likes to produce tiny literals of 1 or 2 bytes. Handle them here. +.lit2b:movsb +.lit1b:movsb + +.check_offset: + test dl,dl ;check match offset mode in token (X bit) + js .rep_match_or_large_offset + + cmp dl,040H ;check if this is a 5 or 9-bit offset (Y bit) + jnb .offset_9_bit + + ;5 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + mov al,020H ;shift Z (offset bit 4) in place + and al,dl + shl al,1 + shl al,1 + get_nybble ;get nybble for offset bits 0-3 + or al,cl ;merge nybble + rol al,1 + xor al,0E1H ;set offset bits 7-5 to 1 + dec ah ;set offset bits 15-8 to 1 + jmp .get_match_length + +.rep_match_or_16_bit: + test dl,020H ;test bit Z (offset bit 8) + jne .repeat_match ;rep-match + + ;16 bit offset: + lodsw ;Get 2-byte match offset + xchg ah,al + jmp .get_match_length + +.offset_9_bit: + ;9 bit offset: + xchg cx,ax ;clear ah - cx is zero from prior rep movs + lodsb ;get 8 bit offset from stream in A + dec ah ;set offset bits 15-8 to 1 + test dl,020H ;test bit Z (offset bit 8) + je .get_match_length + dec ah ;clear bit 8 if Z bit is clear + jmp .get_match_length + +.rep_match_or_large_offset: + cmp dl,0c0H ;check if this is a 13-bit offset + ;or a 16-bit offset/rep match (Y bit) + jnb .rep_match_or_16_bit + + ;13 bit offset: + mov ah,020H ;shift Z (offset bit 12) in place + and ah,dl + shl ah,1 + shl ah,1 + get_nybble ;get nybble for offset bits 8-11 + or ah,cl ;merge nybble + rol ah,1 + xor ah,0E1H ;set offset bits 15-13 to 1 + sub ah,2 ;substract 512 + lodsb ;load match offset bits 0-7 + +.get_match_length: + mov bp,ax ;bp:=offset +.repeat_match: + mov ax,dx ;ax: original token + and al,07H ;isolate match length in token (MMM) + add al,2 ;add MIN_MATCH_SIZE_V2 + + cmp al,09H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ;no, we have full match length from token + + get_nybble ;get extra literals length nybble + add al,cl ;add len from token to nybble + cmp al,018H ;MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ;no, we have full match length from token + + lodsb ;grab extra length byte + add al,018H ;overflow? + jnc .got_matchlen_big ;if not, we have entire (big) length + je .done_decompressing ; detect EOD code + + lodsw ;grab 16-bit length + +;If we're here, we have a larger match copy and can optimize how we do that +.got_matchlen_big: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si now points at back reference in output data + add si,bp +%if HANDLE_WORD_RUN + cmp bp,-2 ;do we have a byte/word run to optimize? + jae .do_run ;perform a run +%else + cmp bp,-1 ;do we have a byte run to optimize? + je .do_run_1 ;perform a byte run +%endif +;You may be tempted to change "jae" to "jge" because DX is a signed number. +;Don't! The total window is 64k, so if you treat this as a signed comparison, +;you will get incorrect results for offsets over 32K. +; +;If we're here, we have a long copy and it isn't byte-overlapping (if it +;overlapped, we'd be in .do_run_1) So, let's copy faster with REP MOVSW. +;This won't affect 8088 that much, but it speeds up 8086 and higher. + shr cx,1 + rep movsw + adc cx,0 + rep movsb + xchg si,ax + mov ds,dx ;restore ds:si + jmp .decode_token ;go decode another token + +;Smaller match copies handled here: +.got_matchlen: + xchg cx,ax ;copy match length into cx + mov dx,ds ;save ds + mov ax,es + mov ds,ax ;ds:=es + xchg si,ax ;dx:ax = old ds:si + mov si,di ;ds:si = back reference in output data + add si,bp + rep movsb ;copy match + xchg si,ax + mov ds,dx ;restore ds:si + jmp .decode_token ;go decode another token + +.done_decompressing: + pop ax ;retrieve the original decompression offset + xchg di,ax ;compute decompressed size + sub ax,di + ret ;done + +%if HANDLE_WORD_RUN +.do_run: + je .do_run_2 ;fall through to byte (common) if not word run +%endif + +.do_run_1: + push ax + lodsb ;load first byte of run into al + mov ah,al + shr cx,1 + rep stosw ;perform word run + adc cx,0 + rep stosb ;finish word run + pop si + mov ds,dx + jmp .decode_token ;go decode another token + +%if HANDLE_WORD_RUN +.do_run_2: + push ax + lodsw ;load first word of run + shr cx,1 + rep stosw ;perform word run + adc cx,0 ;despite 2-byte offset, compressor might + rep stosb ;output odd length. better safe than sorry. + pop si + mov ds,dx + jmp .decode_token ;go decode another token +%endif + +;Speed optimization history (decompression times in microseconds @ 4.77 MHz): +;Compression corpus:shuttle alice robotro rletest largetx linewar ...... .. +;Start of exercise 160828 113311 665900 238507 1053865 1004237 ****** +;add al,val -> al,cl 160813 113296 668721 237484 1053604 1003815 ++-+++ +;sub ah,2 -> dec dec 160907 113585 666744 237484 1056651 1005172 --+*-- rb +;mov ax,cx->xchgcxax 159741 112460 660594 237477 1046770 998323 ++++++ +;unroll get_nibble 152552 106327 621119 237345 982381 942373 ++++++ +;early exit if LL=0 147242 103842 615559 239318 946863 942932 +++-+- +;push/pop->mov/mov 145447 100832 604822 237288 927017 931366 ++++++ +;push/pop->mov/mov(2)143214 98817 592920 239298 908217 910955 +++-++ +;rep stos for -1, -2 143289 102812 617087 237164 942081 940688 ---+-- rb +;larger literal cpys 143214 98817 591940 238296 907237 909657 **++++ +;larger copys & runs 132440 98802 586551 178768 904129 896709 ++++++ :-) +;smaller lit. copies 131991 99131 583933 177760 901824 898308 +-+++- +;swap smal lit compa 131828 99022 585121 177757 901793 894054 ++-*++ +;compare before shif 130587 95970 569908 177753 889221 872461 +++*++ +;getmatchlength base 130587 95970 570634 177753 893536 871556 ...... === +; f->rep_match_or_16 xxxxxx xxxxx 569910 xxxxxx 889266 871435 ..+.++ +; f->rep_match_or_la 129966 94748 566169 xxxxxx 880870 867030 +++.++ +++ +; f->offset_9_bit 132126 95258 568869 xxxxxx 893169 870364 -++.-+ +;final fallthrough 129966 94748 566169 177753 880870 865023 ****** diff --git a/loader/tools/lzsa/asm/x86/decompress_small_v1.asm b/loader/tools/lzsa/asm/x86/decompress_small_v1.asm new file mode 100644 index 0000000..41ce991 --- /dev/null +++ b/loader/tools/lzsa/asm/x86/decompress_small_v1.asm @@ -0,0 +1,120 @@ +; decompress_small_v1.asm - space-efficient decompressor implementation for x86 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 32 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA1 block +; inputs: +; * esi: raw LZSA1 block +; * edi: output buffer +; output: +; * eax: decompressed size +; --------------------------------------------------------------------------- + + %ifndef BIN + global lzsa1_decompress + global _lzsa1_decompress + %endif + +lzsa1_decompress: +_lzsa1_decompress: + pushad + + ;mov edi, [esp+32+4] ; edi = outbuf + ;mov esi, [esp+32+8] ; esi = inbuf + + xor ecx, ecx +.decode_token: + mul ecx + lodsb ; read token byte: O|LLL|MMMM + mov dl, al ; keep token in dl + + and al, 070H ; isolate literals length in token (LLL) + shr al, 4 ; shift literals length into place + + cmp al, 07H ; LITERALS_RUN_LEN? + jne .got_literals ; no, we have the full literals count from the token, go copy + + lodsb ; grab extra length byte + add al, 07H ; add LITERALS_RUN_LEN + jnc .got_literals ; if no overflow, we have the full literals count, go copy + jne .mid_literals + + lodsw ; grab 16-bit extra length + jmp .got_literals + +.mid_literals: + lodsb ; grab single extra length byte + inc ah ; add 256 + +.got_literals: + xchg ecx, eax + rep movsb ; copy cx literals from ds:si to es:di + + test dl, dl ; check match offset size in token (O bit) + js .get_long_offset + + dec ecx + xchg eax, ecx ; clear ah - cx is zero from the rep movsb above + lodsb + jmp .get_match_length + +.get_long_offset: + lodsw ; Get 2-byte match offset + +.get_match_length: + xchg eax, edx ; edx: match offset eax: original token + and al, 0FH ; isolate match length in token (MMMM) + add al, 3 ; add MIN_MATCH_SIZE + + cmp al, 012H ; MATCH_RUN_LEN? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,012H ; add MIN_MATCH_SIZE + MATCH_RUN_LEN + jnc .got_matchlen ; if no overflow, we have the entire length + jne .mid_matchlen + + lodsw ; grab 16-bit length + test eax, eax ; bail if we hit EOD + je .done_decompressing + jmp .got_matchlen + +.mid_matchlen: + lodsb ; grab single extra length byte + inc ah ; add 256 + +.got_matchlen: + xchg ecx, eax ; copy match length into ecx + xchg esi, eax + mov esi, edi ; esi now points at back reference in output data + movsx edx, dx ; sign-extend dx to 32-bits. + add esi, edx + rep movsb ; copy match + xchg esi, eax ; restore esi + jmp .decode_token ; go decode another token + +.done_decompressing: + sub edi, [esp+32+4] + mov [esp+28], edi ; eax = decompressed size + popad + ret ; done diff --git a/loader/tools/lzsa/asm/x86/decompress_small_v2.asm b/loader/tools/lzsa/asm/x86/decompress_small_v2.asm new file mode 100644 index 0000000..fe185c1 --- /dev/null +++ b/loader/tools/lzsa/asm/x86/decompress_small_v2.asm @@ -0,0 +1,181 @@ +; decompress_small_v2.asm - space-efficient decompressor implementation for x86 +; +; Copyright (C) 2019 Emmanuel Marty +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + + segment .text + bits 32 + +; --------------------------------------------------------------------------- +; Decompress raw LZSA2 block +; inputs: +; * esi: raw LZSA2 block +; * edi: output buffer +; output: +; * eax: decompressed size +; --------------------------------------------------------------------------- + + %ifndef BIN + global lzsa2_decompress + global _lzsa2_decompress + %endif + +lzsa2_decompress: +_lzsa2_decompress: + pushad + + ;mov edi, [esp+32+4] ; edi = outbuf + ;mov esi, [esp+32+8] ; esi = inbuf + + xor ecx, ecx + xor ebx, ebx ; ebx = 0100H + inc bh + xor ebp, ebp + +.decode_token: + mul ecx + lodsb ; read token byte: XYZ|LL|MMMM + mov dl, al ; keep token in dl + + and al, 018H ; isolate literals length in token (LL) + shr al, 3 ; shift literals length into place + + cmp al, 03H ; LITERALS_RUN_LEN_V2? + jne .got_literals ; no, we have the full literals count from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al, cl ; add len from token to nibble + cmp al, 012H ; LITERALS_RUN_LEN_V2 + 15 ? + jne .got_literals ; if not, we have the full literals count, go copy + + lodsb ; grab extra length byte + add al,012H ; overflow? + jnc .got_literals ; if not, we have the full literals count, go copy + + lodsw ; grab 16-bit extra length + +.got_literals: + xchg ecx, eax + rep movsb ; copy ecx literals from esi to edi + + test dl, 0C0h ; check match offset mode in token (X bit) + js .rep_match_or_large_offset + + ;;cmp dl,040H ; check if this is a 5 or 9-bit offset (Y bit) + ; discovered via the test with bit 6 set + xchg ecx, eax ; clear ah - cx is zero from the rep movsb above + jne .offset_9_bit + + ; 5 bit offset + cmp dl, 020H ; test bit 5 + call .get_nibble_x + jmp .dec_offset_top + +.offset_9_bit: ; 9 bit offset + lodsb ; get 8 bit offset from stream in A + dec ah ; set offset bits 15-8 to 1 + test dl, 020H ; test bit Z (offset bit 8) + je .get_match_length +.dec_offset_top: + dec ah ; clear bit 8 if Z bit is clear + ; or set offset bits 15-8 to 1 + jmp .get_match_length + +.rep_match_or_large_offset: + ;;cmp dl,0c0H ; check if this is a 13-bit offset or a 16-bit offset/rep match (Y bit) + jpe .rep_match_or_16_bit + + ; 13 bit offset + + cmp dl, 0A0H ; test bit 5 (knowing that bit 7 is also set) + xchg ah, al + call .get_nibble_x + sub al, 2 ; substract 512 + jmp .get_match_length_1 + +.rep_match_or_16_bit: + test dl, 020H ; test bit Z (offset bit 8) + jne .repeat_match ; rep-match + + ; 16 bit offset + lodsb ; Get 2-byte match offset + +.get_match_length_1: + xchg ah, al + lodsb ; load match offset bits 0-7 + +.get_match_length: + xchg ebp, eax ; ebp: offset +.repeat_match: + xchg eax, edx ; ax: original token + and al, 07H ; isolate match length in token (MMM) + add al, 2 ; add MIN_MATCH_SIZE_V2 + + cmp al, 09H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + call .get_nibble ; get extra literals length nibble + add al, cl ; add len from token to nibble + cmp al, 018H ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + jne .got_matchlen ; no, we have the full match length from the token, go copy + + lodsb ; grab extra length byte + add al,018H ; overflow? + jnc .got_matchlen ; if not, we have the entire length + je .done_decompressing ; detect EOD code + + lodsw ; grab 16-bit length + +.got_matchlen: + xchg ecx, eax ; copy match length into ecx + xchg esi, eax + movsx ebp, bp ; sign-extend bp to 32-bits + lea esi,[ebp+edi] ; esi now points at back reference in output data + rep movsb ; copy match + xchg esi, eax ; restore esi + jmp .decode_token ; go decode another token + +.done_decompressing: + sub edi, [esp+32+4] + mov [esp+28], edi + popad + ret ; done + +.get_nibble_x: + cmc ; carry set if bit 4 was set + rcr al, 1 + call .get_nibble ; get nibble for offset bits 0-3 + or al, cl ; merge nibble + rol al, 1 + xor al, 0E1H ; set offset bits 7-5 to 1 + ret + +.get_nibble: + neg bh ; nibble ready? + jns .has_nibble + + xchg ebx, eax + lodsb ; load two nibbles + xchg ebx, eax + +.has_nibble: + mov cl, 4 ; swap 4 high and low bits of nibble + ror bl, cl + mov cl, 0FH + and cl, bl + ret diff --git a/loader/tools/lzsa/asm/z80/unlzsa1_fast.asm b/loader/tools/lzsa/asm/z80/unlzsa1_fast.asm new file mode 100644 index 0000000..79b5c61 --- /dev/null +++ b/loader/tools/lzsa/asm/z80/unlzsa1_fast.asm @@ -0,0 +1,201 @@ +; +; Speed-optimized LZSA1 decompressor by spke & uniabis (109 bytes) +; +; ver.00 by spke for LZSA 0.5.4 (03-24/04/2019, 134 bytes); +; ver.01 by spke for LZSA 0.5.6 (25/04/2019, 110(-24) bytes, +0.2% speed); +; ver.02 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.03 by uniabis (30/07/2019, 109(-1) bytes, +3.5% speed); +; ver.04 by spke (31/07/2019, small re-organization of macros); +; ver.05 by uniabis (22/08/2019, 107(-2) bytes, same speed); +; ver.06 by spke for LZSA 1.0.7 (27/08/2019, 111(+4) bytes, +2.1% speed); +; ver.07 by spke for LZSA 1.1.0 (25/09/2019, added full revision history); +; ver.08 by spke for LZSA 1.1.2 (22/10/2019, re-organized macros and added an option for unrolled copying of long matches); +; ver.09 by spke for LZSA 1.2.1 (02/01/2020, 109(-2) bytes, same speed) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f1 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f1 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE UNROLL_LONG_MATCHES ; uncomment for faster decompression of very compressible data (+57 bytes) +; DEFINE BACKWARD_DECOMPRESS + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO COPY1 + ldi + ENDM + + MACRO COPYBC + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : ld a,e : sub l : ld l,a + ld a,d : sbc h : ld h,a ; 4*4+3*4 = 28t / 7 bytes + ENDM + + MACRO COPY1 + ldd + ENDM + + MACRO COPYBC + lddr + ENDM + + ENDIF + +@DecompressLZSA1: + ld b,0 : jr ReadToken + +NoLiterals: xor (hl) : NEXT_HL : jp m,LongOffset + +ShortOffset: push de : ld e,(hl) : ld d,#FF + + ; short matches have length 0+3..14+3 + add 3 : cp 15+3 : jr nc,LongerMatch + + ; placed here this saves a JP per iteration +CopyMatch: ld c,a +.UseC NEXT_HL : ex (sp),hl ; BC = len, DE = offset, HL = dest, SP ->[dest,src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest-offset, SP->[src] + COPY1 : COPY1 : COPYBC ; BC = 0, DE = dest +.popSrc pop hl ; HL = src + +ReadToken: ; first a byte token "O|LLL|MMMM" is read from the stream, + ; where LLL is the number of literals and MMMM is + ; a length of the match that follows after the literals + ld a,(hl) : and #70 : jr z,NoLiterals + + cp #70 : jr z,MoreLiterals ; LLL=7 means 7+ literals... + rrca : rrca : rrca : rrca : ld c,a ; LLL<7 means 0..6 literals... + + ld a,(hl) : NEXT_HL + COPYBC + + ; the top bit of token is set if the offset contains two bytes + and #8F : jp p,ShortOffset + +LongOffset: ; read second byte of the offset + push de : ld e,(hl) : NEXT_HL : ld d,(hl) + add -128+3 : cp 15+3 : jp c,CopyMatch + + IFNDEF UNROLL_LONG_MATCHES + + ; MMMM=15 indicates a multi-byte number of literals +LongerMatch: NEXT_HL : add (hl) : jr nc,CopyMatch + + ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,CopyMatch.UseC +.code0 NEXT_HL : ld b,(hl) + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + ld a,b : or c : jr nz,CopyMatch.UseC + pop de : ret + + ELSE + + ; MMMM=15 indicates a multi-byte number of literals +LongerMatch: NEXT_HL : add (hl) : jr c,VeryLongMatch + + ld c,a +.UseC NEXT_HL : ex (sp),hl + ADD_OFFSET + COPY1 : COPY1 + + ; this is an unrolled equivalent of LDIR + xor a : sub c + and 16-1 : add a + ld (.jrOffset),a : jr nz,$+2 +.jrOffset EQU $-1 +.fastLDIR DUP 16 + COPY1 + EDUP + jp pe,.fastLDIR + jp CopyMatch.popSrc + +VeryLongMatch: ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,LongerMatch.UseC +.code0 NEXT_HL : ld b,(hl) + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + ld a,b : or c : jr nz,LongerMatch.UseC + pop de : ret + + ENDIF + +MoreLiterals: ; there are three possible situations here + xor (hl) : NEXT_HL : exa + ld a,7 : add (hl) : jr c,ManyLiterals + +CopyLiterals: ld c,a +.UseC NEXT_HL : COPYBC + + exa : jp p,ShortOffset : jr LongOffset + +ManyLiterals: +.code1 ld b,a : NEXT_HL : ld c,(hl) : jr nz,CopyLiterals.UseC +.code0 NEXT_HL : ld b,(hl) : jr CopyLiterals.UseC + + diff --git a/loader/tools/lzsa/asm/z80/unlzsa1_small.asm b/loader/tools/lzsa/asm/z80/unlzsa1_small.asm new file mode 100644 index 0000000..8592f92 --- /dev/null +++ b/loader/tools/lzsa/asm/z80/unlzsa1_small.asm @@ -0,0 +1,135 @@ +; +; Size-optimized LZSA1 decompressor by spke & uniabis (67 bytes) +; +; ver.00 by spke for LZSA 0.5.4 (23/04/2019, 69 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by uniabis (30/07/2019, 68(-1) bytes, +3.2% speed); +; ver.03 by spke for LZSA 1.0.7 (31/07/2019, small re-organization of macros); +; ver.04 by spke (06/08/2019, 67(-1) bytes, -1.2% speed); +; ver.05 by spke for LZSA 1.1.0 (25/09/2019, added full revision history) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f1 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f1 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA1 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE BACKWARD_DECOMPRESS + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO BLOCKCOPY + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + push hl : or a : sbc hl,de : pop de ; 11+4+15+10 = 40t / 5 bytes + ENDM + + MACRO BLOCKCOPY + lddr + ENDM + + ENDIF + +@DecompressLZSA1: + ld b,0 + + ; first a byte token "O|LLL|MMMM" is read from the stream, + ; where LLL is the number of literals and MMMM is + ; a length of the match that follows after the literals +ReadToken: ld a,(hl) : NEXT_HL : push af + and #70 : jr z,NoLiterals + + rrca : rrca : rrca : rrca ; LLL<7 means 0..6 literals... + cp #07 : call z,ReadLongBA ; LLL=7 means 7+ literals... + + ld c,a : BLOCKCOPY + + ; next we read the low byte of the -offset +NoLiterals: pop af : push de : ld e,(hl) : NEXT_HL : ld d,#FF + ; the top bit of token is set if + ; the offset contains the high byte as well + or a : jp p,ShortOffset + +LongOffset: ld d,(hl) : NEXT_HL + + ; last but not least, the match length is read +ShortOffset: and #0F : add 3 ; MMMM<15 means match lengths 0+3..14+3 + cp 15+3 : call z,ReadLongBA ; MMMM=15 means lengths 14+3+ + ld c,a + + ex (sp),hl ; BC = len, DE = -offset, HL = dest, SP -> [src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest+(-offset), SP -> [src] + BLOCKCOPY ; BC = 0, DE = dest + pop hl : jr ReadToken ; HL = src + + ; a standard routine to read extended codes + ; into registers B (higher byte) and A (lower byte). +ReadLongBA: add (hl) : NEXT_HL : ret nc + + ; the codes are designed to overflow; + ; the overflow value 1 means read 1 extra byte + ; and overflow value 0 means read 2 extra bytes +.code1: ld b,a : ld a,(hl) : NEXT_HL : ret nz +.code0: ld c,a : ld b,(hl) : NEXT_HL + + ; the two-byte match length equal to zero + ; designates the end-of-data marker + or b : ld a,c : ret nz + pop de : pop de : ret + diff --git a/loader/tools/lzsa/asm/z80/unlzsa2_fast.asm b/loader/tools/lzsa/asm/z80/unlzsa2_fast.asm new file mode 100644 index 0000000..57026da --- /dev/null +++ b/loader/tools/lzsa/asm/z80/unlzsa2_fast.asm @@ -0,0 +1,281 @@ +; +; Speed-optimized LZSA2 decompressor by spke & uniabis (216 bytes) +; +; ver.00 by spke for LZSA 1.0.0 (02-07/06/2019, 218 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by spke for LZSA 1.0.6 (27/07/2019, fixed a bug in the backward decompressor); +; ver.03 by uniabis (30/07/2019, 213(-5) bytes, +3.8% speed and support for Hitachi HD64180); +; ver.04 by spke for LZSA 1.0.7 (01/08/2019, 214(+1) bytes, +0.2% speed and small re-organization of macros); +; ver.05 by spke (27/08/2019, 216(+2) bytes, +1.1% speed); +; ver.06 by spke for LZSA 1.1.0 (26/09/2019, added full revision history); +; ver.07 by spke for LZSA 1.1.1 (10/10/2019, +0.2% speed and an option for unrolled copying of long matches) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f2 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f2 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA2 compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. + +; DEFINE UNROLL_LONG_MATCHES ; uncomment for faster decompression of very compressible data (+38 bytes) +; DEFINE BACKWARD_DECOMPRESS ; uncomment for data compressed with option -b +; DEFINE HD64180 ; uncomment for systems using Hitachi HD64180 + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO COPY1 + ldi + ENDM + + MACRO COPYBC + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : ld a,e : sub l : ld l,a + ld a,d : sbc h : ld h,a ; 4*4+3*4 = 28t / 7 bytes + ENDM + + MACRO COPY1 + ldd + ENDM + + MACRO COPYBC + lddr + ENDM + + ENDIF + + IFNDEF HD64180 + + MACRO LD_IX_DE + ld ixl,e : ld ixh,d + ENDM + + MACRO LD_DE_IX + ld e,ixl : ld d,ixh + ENDM + + ELSE + + MACRO LD_IX_DE + push de : pop ix + ENDM + + MACRO LD_DE_IX + push ix : pop de + ENDM + + ENDIF + +@DecompressLZSA2: + ; A' stores next nibble as %1111.... or assumed to contain trash + ; B is assumed to be 0 + ld b,0 : scf : exa : jr ReadToken + + + + +ManyLiterals: ld a,18 : add (hl) : NEXT_HL : jr nc,CopyLiterals + ld c,(hl) : NEXT_HL + ld a,b : ld b,(hl) + jr ReadToken.NEXTHLuseBC + + + + +MoreLiterals: ld b,(hl) : NEXT_HL + scf : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ;sub #F0-3 : cp 15+3 : jr z,ManyLiterals + inc a : jr z,ManyLiterals : sub #F0-3+1 + +CopyLiterals: ld c,a : ld a,b : ld b,0 + COPYBC + push de : or a : jp p,CASE0xx ;: jr CASE1xx + + cp %11000000 : jr c,CASE10x + +CASE11x cp %11100000 : jr c,CASE110 + + ; "111": repeated offset +CASE111: LD_DE_IX : jr MatchLen + + + + +Literals0011: jr nz,MoreLiterals + + ; if "LL" of the byte token is equal to 0, + ; there are no literals to copy +NoLiterals: or (hl) : NEXT_HL + push de : jp m,CASE1xx + + ; short (5 or 9 bit long) offsets +CASE0xx ld d,#FF : cp %01000000 : jr c,CASE00x + + ; "01x": the case of the 9-bit offset +CASE01x: cp %01100000 : rl d + +ReadOffsetE ld e,(hl) : NEXT_HL + +SaveOffset: LD_IX_DE + +MatchLen: inc a : and %00000111 : jr z,LongerMatch : inc a + +CopyMatch: ld c,a +.useC ex (sp),hl ; BC = len, DE = offset, HL = dest, SP ->[dest,src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest-offset, SP->[src] + COPY1 + COPYBC +.popSrc pop hl + + ; compressed data stream contains records + ; each record begins with the byte token "XYZ|LL|MMM" +ReadToken: ld a,(hl) : and %00011000 : jp pe,Literals0011 ; process the cases 00 and 11 separately + + rrca : rrca : rrca + + ld c,a : ld a,(hl) ; token is re-read for further processing +.NEXTHLuseBC NEXT_HL + COPYBC + + ; the token and literals are followed by the offset + push de : or a : jp p,CASE0xx + +CASE1xx cp %11000000 : jr nc,CASE11x + + ; "10x": the case of the 13-bit offset +CASE10x: ld c,a : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ld d,a : ld a,c + cp %10100000 : dec d : rl d : jr ReadOffsetE + + + + ; "110": 16-bit offset +CASE110: ld d,(hl) : NEXT_HL : jr ReadOffsetE + + + + + ; "00x": the case of the 5-bit offset +CASE00x: ld c,a : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate ld e,a : ld a,c + cp %00100000 : rl e : jp SaveOffset + + + + + +LongerMatch: scf : exa : jr nc,.noUpdate + + ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca + +.noUpdate sub #F0-9 : cp 15+9 : jr c,CopyMatch + + IFNDEF UNROLL_LONG_MATCHES + +LongMatch: add (hl) : NEXT_HL : jr nc,CopyMatch + ld c,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : jr nz,CopyMatch.useC + pop de : ret + + ELSE + +LongMatch: add (hl) : NEXT_HL : jr c,VeryLongMatch + + ld c,a +.useC ex (sp),hl + ADD_OFFSET + COPY1 + + ; this is an unrolled equivalent of LDIR + xor a : sub c + and 8-1 : add a + ld (.jrOffset),a : jr nz,$+2 +.jrOffset EQU $-1 +.fastLDIR DUP 8 + COPY1 + EDUP + jp pe,.fastLDIR + jp CopyMatch.popSrc + +VeryLongMatch: ld c,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : jr nz,LongMatch.useC + pop de : ret + + ENDIF + + + + + diff --git a/loader/tools/lzsa/asm/z80/unlzsa2_small.asm b/loader/tools/lzsa/asm/z80/unlzsa2_small.asm new file mode 100644 index 0000000..0544c9a --- /dev/null +++ b/loader/tools/lzsa/asm/z80/unlzsa2_small.asm @@ -0,0 +1,187 @@ +; +; Size-optimized LZSA2 decompressor by spke & uniabis (139 bytes) +; +; ver.00 by spke for LZSA 1.0.0 (02-09/06/2019, 145 bytes); +; ver.01 by spke for LZSA 1.0.5 (24/07/2019, added support for backward decompression); +; ver.02 by uniabis (30/07/2019, 144(-1) bytes, +3.3% speed and support for Hitachi HD64180); +; ver.03 by spke for LZSA 1.0.7 (01/08/2019, 140(-4) bytes, -1.4% speed and small re-organization of macros); +; ver.04 by spke for LZSA 1.1.0 (26/09/2019, removed usage of IY, added full revision history) +; ver.05 by spke for LZSA 1.1.1 (11/10/2019, 139(-1) bytes, +0.1% speed) +; +; The data must be compressed using the command line compressor by Emmanuel Marty +; The compression is done as follows: +; +; lzsa.exe -f2 -r +; +; where option -r asks for the generation of raw (frame-less) data. +; +; The decompression is done in the standard way: +; +; ld hl,FirstByteOfCompressedData +; ld de,FirstByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; Backward compression is also supported; you can compress files backward using: +; +; lzsa.exe -f2 -r -b +; +; and decompress the resulting files using: +; +; ld hl,LastByteOfCompressedData +; ld de,LastByteOfMemoryForDecompressedData +; call DecompressLZSA2 +; +; (do not forget to uncomment the BACKWARD_DECOMPRESS option in the decompressor). +; +; Of course, LZSA2 compression algorithms are (c) 2019 Emmanuel Marty, +; see https://github.com/emmanuel-marty/lzsa for more information +; +; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; + +; DEFINE BACKWARD_DECOMPRESS ; uncomment for data compressed with option -b +; DEFINE HD64180 ; uncomment for systems using Hitachi HD64180 + + IFNDEF BACKWARD_DECOMPRESS + + MACRO NEXT_HL + inc hl + ENDM + + MACRO ADD_OFFSET + ex de,hl : add hl,de + ENDM + + MACRO BLOCKCOPY + ldir + ENDM + + ELSE + + MACRO NEXT_HL + dec hl + ENDM + + MACRO ADD_OFFSET + push hl : or a : sbc hl,de : pop de ; 11+4+15+10 = 40t / 5 bytes + ENDM + + MACRO BLOCKCOPY + lddr + ENDM + + ENDIF + + IFNDEF HD64180 + + MACRO LD_IX_DE + ld ixl,e : ld ixh,d + ENDM + + MACRO LD_DE_IX + ld e,ixl : ld d,ixh + ENDM + + ELSE + + MACRO LD_IX_DE + push de : pop ix + ENDM + + MACRO LD_DE_IX + push ix : pop de + ENDM + + ENDIF + +@DecompressLZSA2: + xor a : ld b,a : exa : jr ReadToken + +CASE00x: call ReadNibble + ld e,a : ld a,c + cp %00100000 : rl e : jr SaveOffset + +CASE0xx ld d,#FF : cp %01000000 : jr c,CASE00x + +CASE01x: cp %01100000 : rl d + +OffsetReadE: ld e,(hl) : NEXT_HL + +SaveOffset: LD_IX_DE + +MatchLen: and %00000111 : add 2 : cp 9 : call z,ExtendedCode + +CopyMatch: ld c,a + ex (sp),hl ; BC = len, DE = -offset, HL = dest, SP -> [src] + ADD_OFFSET ; BC = len, DE = dest, HL = dest+(-offset), SP -> [src] + BLOCKCOPY ; BC = 0, DE = dest + pop hl ; HL = src + +ReadToken: ld a,(hl) : NEXT_HL : push af + and %00011000 : jr z,NoLiterals + + rrca : rrca : rrca + call pe,ExtendedCode + + ld c,a + BLOCKCOPY + +NoLiterals: pop af : push de + or a : jp p,CASE0xx + +CASE1xx cp %11000000 : jr nc,CASE11x + +CASE10x: call ReadNibble + ld d,a : ld a,c + cp %10100000 ;: rl d + dec d : rl d : DB #CA ; jr OffsetReadE ; #CA is JP Z,.. to skip all commands in CASE110 before jr OffsetReadE + +CASE110: ld d,(hl) : NEXT_HL : jr OffsetReadE + +CASE11x cp %11100000 : jr c,CASE110 + +CASE111: LD_DE_IX : jr MatchLen + +ExtendedCode: call ReadNibble : inc a : jr z,ExtraByte + sub #F0+1 : add c : ret +ExtraByte ld a,15 : add c : add (hl) : NEXT_HL : ret nc + ld a,(hl) : NEXT_HL + ld b,(hl) : NEXT_HL : ret nz + pop de : pop de ; RET is not needed, because RET from ReadNibble is sufficient + +ReadNibble: ld c,a : xor a : exa : ret m +UpdateNibble ld a,(hl) : or #F0 : exa + ld a,(hl) : NEXT_HL : or #0F + rrca : rrca : rrca : rrca : ret + + + + + + + + + + + + + + + + diff --git a/loader/tools/lzsa/pareto_graph.png b/loader/tools/lzsa/pareto_graph.png new file mode 100644 index 0000000..5ab3a91 Binary files /dev/null and b/loader/tools/lzsa/pareto_graph.png differ diff --git a/loader/tools/lzsa/src/dictionary.c b/loader/tools/lzsa/src/dictionary.c new file mode 100644 index 0000000..fbc4f52 --- /dev/null +++ b/loader/tools/lzsa/src/dictionary.c @@ -0,0 +1,101 @@ +/* + * dictionary.c - dictionary implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "lib.h" + +/** + * Load dictionary contents + * + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param ppDictionaryData pointer to returned dictionary contents, or NULL for none + * @param pDictionaryDataSize pointer to returned size of dictionary contents, or 0 + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +int lzsa_dictionary_load(const char *pszDictionaryFilename, void **ppDictionaryData, int *pDictionaryDataSize) { + unsigned char *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + + if (pszDictionaryFilename) { + pDictionaryData = (unsigned char *)malloc(BLOCK_SIZE); + if (!pDictionaryData) { + return LZSA_ERROR_MEMORY; + } + + FILE *pDictionaryFile = fopen(pszDictionaryFilename, "rb"); + if (!pDictionaryFile) { + free(pDictionaryData); + pDictionaryData = NULL; + return LZSA_ERROR_DICTIONARY; + } + + fseek(pDictionaryFile, 0, SEEK_END); +#ifdef _WIN32 + __int64 nDictionaryFileSize = _ftelli64(pDictionaryFile); +#else + off_t nDictionaryFileSize = ftello(pDictionaryFile); +#endif + if (nDictionaryFileSize > BLOCK_SIZE) { + /* Use the last BLOCK_SIZE bytes of the dictionary */ + fseek(pDictionaryFile, -BLOCK_SIZE, SEEK_END); + } + else { + fseek(pDictionaryFile, 0, SEEK_SET); + } + + nDictionaryDataSize = (int)fread(pDictionaryData, 1, BLOCK_SIZE, pDictionaryFile); + if (nDictionaryDataSize < 0) + nDictionaryDataSize = 0; + + fclose(pDictionaryFile); + pDictionaryFile = NULL; + } + + *ppDictionaryData = pDictionaryData; + *pDictionaryDataSize = nDictionaryDataSize; + return LZSA_OK; +} + +/** + * Free dictionary contents + * + * @param ppDictionaryData pointer to pointer to dictionary contents + */ +void lzsa_dictionary_free(void **ppDictionaryData) { + if (*ppDictionaryData) { + free(*ppDictionaryData); + *ppDictionaryData = NULL; + } +} diff --git a/loader/tools/lzsa/src/dictionary.h b/loader/tools/lzsa/src/dictionary.h new file mode 100644 index 0000000..a363564 --- /dev/null +++ b/loader/tools/lzsa/src/dictionary.h @@ -0,0 +1,64 @@ +/* + * dictionary.h - dictionary definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _DICTIONARY_H +#define _DICTIONARY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Load dictionary contents + * + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param ppDictionaryData pointer to returned dictionary contents, or NULL for none + * @param pDictionaryDataSize pointer to returned size of dictionary contents, or 0 + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +int lzsa_dictionary_load(const char *pszDictionaryFilename, void **ppDictionaryData, int *pDictionaryDataSize); + +/** + * Free dictionary contents + * + * @param ppDictionaryData pointer to pointer to dictionary contents + */ +void lzsa_dictionary_free(void **ppDictionaryData); + +#ifdef __cplusplus +} +#endif + +#endif /* _DICTIONARY_H */ diff --git a/loader/tools/lzsa/src/expand_block_v1.c b/loader/tools/lzsa/src/expand_block_v1.c new file mode 100644 index 0000000..c248fb3 --- /dev/null +++ b/loader/tools/lzsa/src/expand_block_v1.c @@ -0,0 +1,224 @@ +/* + * expand_block_v1.c - LZSA1 block decompressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "expand_block_v1.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE int lzsa_build_literals_len_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int *nLiterals) { + unsigned int nByte; + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + nByte = *pInBlock++; + (*nLiterals) += nByte; + + if (nByte == 250) { + if (pInBlock < pInBlockEnd) { + (*nLiterals) = 256 + ((unsigned int)*pInBlock++); + } + else { + return -1; + } + } + else if (nByte == 249) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nLiterals) = ((unsigned int)*pInBlock++); + (*nLiterals) |= (((unsigned int)*pInBlock++) << 8); + } + else { + return -1; + } + } + + *ppInBlock = pInBlock; + return 0; + } + else { + return -1; + } +} + +static inline FORCE_INLINE int lzsa_build_match_len_v1(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, unsigned int *nMatchLen) { + unsigned int nByte; + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + nByte = *pInBlock++; + (*nMatchLen) += nByte; + + if (nByte == 239) { + if (pInBlock < pInBlockEnd) { + (*nMatchLen) = 256 + ((unsigned int)*pInBlock++); + } + else { + return -1; + } + } + else if (nByte == 238) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nMatchLen) = ((unsigned int)*pInBlock++); + (*nMatchLen) |= (((unsigned int)*pInBlock++) << 8); + } + else { + return -1; + } + } + + *ppInBlock = pInBlock; + return 0; + } + else { + return -1; + } +} + +/** + * Decompress one LZSA1 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) { + const unsigned char *pInBlockEnd = pInBlock + nBlockSize; + unsigned char *pCurOutData = pOutData + nOutDataOffset; + const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize; + const unsigned char *pOutDataFastEnd = pOutDataEnd - 18; + + while (pInBlock < pInBlockEnd) { + const unsigned char token = *pInBlock++; + unsigned int nLiterals = (unsigned int)((token & 0x70) >> 4); + + if (nLiterals != LITERALS_RUN_LEN_V1 && (pInBlock + 8) <= pInBlockEnd && pCurOutData < pOutDataFastEnd) { + memcpy(pCurOutData, pInBlock, 8); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + if (nLiterals == LITERALS_RUN_LEN_V1) { + if (lzsa_build_literals_len_v1(&pInBlock, pInBlockEnd, &nLiterals)) + return -1; + } + + if (nLiterals != 0) { + if ((pInBlock + nLiterals) <= pInBlockEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInBlock, nLiterals); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + } + } + + if ((pInBlock + 1) < pInBlockEnd) { /* The last token in the block does not include match information */ + unsigned int nMatchOffset; + + nMatchOffset = ((unsigned int)(*pInBlock++)) ^ 0xff; + if (token & 0x80) { + nMatchOffset |= (((unsigned int)(*pInBlock++)) << 8) ^ 0xff00; + } + nMatchOffset++; + + const unsigned char *pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + unsigned int nMatchLen = (unsigned int)(token & 0x0f); + if (nMatchLen != MATCH_RUN_LEN_V1 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd && (pSrc + 18) <= pOutDataEnd) { + memcpy(pCurOutData, pSrc, 8); + memcpy(pCurOutData + 8, pSrc + 8, 8); + memcpy(pCurOutData + 16, pSrc + 16, 2); + pCurOutData += (MIN_MATCH_SIZE_V1 + nMatchLen); + } + else { + nMatchLen += MIN_MATCH_SIZE_V1; + if (nMatchLen == (MATCH_RUN_LEN_V1 + MIN_MATCH_SIZE_V1)) { + if (lzsa_build_match_len_v1(&pInBlock, pInBlockEnd, &nMatchLen)) + return -1; + if (nMatchLen == 0) + break; + } + + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + /* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */ + + if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) { + const unsigned char *pCopySrc = pSrc; + unsigned char *pCopyDst = pCurOutData; + const unsigned char *pCopyEndDst = pCurOutData + nMatchLen; + + do { + memcpy(pCopyDst, pCopySrc, 16); + pCopySrc += 16; + pCopyDst += 16; + } while (pCopyDst < pCopyEndDst); + + pCurOutData += nMatchLen; + } + else { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + } + else { + return -1; + } + } + else { + return -1; + } + } + } + else { + return -1; + } + } + } + + return (int)(pCurOutData - (pOutData + nOutDataOffset)); +} diff --git a/loader/tools/lzsa/src/expand_block_v1.h b/loader/tools/lzsa/src/expand_block_v1.h new file mode 100644 index 0000000..a1d5651 --- /dev/null +++ b/loader/tools/lzsa/src/expand_block_v1.h @@ -0,0 +1,49 @@ +/* + * expand_block_v1.h - LZSA1 block decompressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_BLOCK_V1_H +#define _EXPAND_BLOCK_V1_H + +/** + * Decompress one LZSA1 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v1(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize); + +#endif /* _EXPAND_BLOCK_V1_H */ diff --git a/loader/tools/lzsa/src/expand_block_v2.c b/loader/tools/lzsa/src/expand_block_v2.c new file mode 100644 index 0000000..d208676 --- /dev/null +++ b/loader/tools/lzsa/src/expand_block_v2.c @@ -0,0 +1,249 @@ +/* + * expand_block_v2.c - LZSA2 block decompressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "format.h" +#include "expand_block_v2.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE unsigned int lzsa_get_nibble_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, int *nCurNibbles, unsigned char *nibbles, unsigned int *nValue) { + if ((*nCurNibbles ^= 1) != 0) { + const unsigned char *pInBlock = *ppInBlock; + if (pInBlock < pInBlockEnd) { + (*nibbles) = *pInBlock++; + *ppInBlock = pInBlock; + (*nValue) = ((unsigned int)((*nibbles) & 0xf0)) >> 4; + return 0; + } + else { + return -1; + } + } + + (*nValue) = (unsigned int)((*nibbles) & 0x0f); + return 0; +} + +static inline FORCE_INLINE int lzsa_build_len_v2(const unsigned char **ppInBlock, const unsigned char *pInBlockEnd, int *nCurNibbles, unsigned char *nibbles, unsigned int *nLength) { + unsigned int nValue; + + if (!lzsa_get_nibble_v2(ppInBlock, pInBlockEnd, nCurNibbles, nibbles, &nValue)) { + (*nLength) += nValue; + + if (nValue == 15) { + const unsigned char *pInBlock = *ppInBlock; + + if (pInBlock < pInBlockEnd) { + (*nLength) += ((unsigned int)*pInBlock++); + + if ((*nLength) == 257) { + if ((pInBlock + 1) < pInBlockEnd) { + (*nLength) = ((unsigned int)*pInBlock++) << 8; + (*nLength) |= ((unsigned int)*pInBlock++); + } + else { + return -1; + } + } + else if ((*nLength) == 256) { + (*nLength) = 0; + } + } + else { + return -1; + } + + *ppInBlock = pInBlock; + } + + return 0; + } + else { + return -1; + } +} + +/** + * Decompress one LZSA2 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize) { + const unsigned char *pInBlockEnd = pInBlock + nBlockSize; + unsigned char *pCurOutData = pOutData + nOutDataOffset; + const unsigned char *pOutDataEnd = pCurOutData + nBlockMaxSize; + const unsigned char *pOutDataFastEnd = pOutDataEnd - 20; + int nCurNibbles = 0; + unsigned char nibbles; + int nMatchOffset = 0; + + while (pInBlock < pInBlockEnd) { + const unsigned char token = *pInBlock++; + unsigned int nLiterals = (unsigned int)((token >> LITERALS_LEN_POS_V2) & LITERALS_RUN_LEN_V2); + + if (nLiterals != LITERALS_RUN_LEN_V2 && (pInBlock + 4) <= pInBlockEnd && pCurOutData < pOutDataFastEnd) { + memcpy(pCurOutData, pInBlock, 4); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + if (nLiterals == LITERALS_RUN_LEN_V2) { + if (lzsa_build_len_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nLiterals)) + return -1; + } + + if (nLiterals != 0) { + if ((pInBlock + nLiterals) <= pInBlockEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInBlock, nLiterals); + pInBlock += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + } + } + + if (pInBlock < pInBlockEnd) { /* The last token in the block does not include match information */ + unsigned char nOffsetMode = token & 0xc0; + unsigned int nValue; + + if ((nOffsetMode & 0x80) == 0x00) { + if ((nOffsetMode & 0x20) == 0x00) { + /* 5 bit offset */ + if (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nValue)) + return -1; + nMatchOffset = nValue << 1; + nMatchOffset |= ((token & 0x40) >> 6); + nMatchOffset ^= 0x1e; + nMatchOffset++; + } + else { + /* 13 bit offset */ + if (lzsa_get_nibble_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nValue)) + return -1; + nMatchOffset = (unsigned int)(*pInBlock++); + nMatchOffset |= (nValue << 9); + nMatchOffset |= (((unsigned int)(token & 0x40)) << 2); + nMatchOffset ^= 0x1eff; + nMatchOffset += (512 + 1); + } + } + else { + if ((nOffsetMode & 0xc0) == 0x80) { + /* 9 bit offset */ + nMatchOffset = (unsigned int)(*pInBlock++); + nMatchOffset |= (((unsigned int)(token & 0x20)) << 3); + nMatchOffset ^= 0x0ff; + nMatchOffset++; + } + if ((nOffsetMode & 0xe0) == 0xc0) { + /* 16 bit offset */ + nMatchOffset = (((unsigned int)(*pInBlock++)) << 8); + if (pInBlock >= pInBlockEnd) return -1; + nMatchOffset |= (unsigned int)(*pInBlock++); + nMatchOffset ^= 0xffff; + nMatchOffset++; + } + } + + const unsigned char *pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + unsigned int nMatchLen = (unsigned int)((token >> MATCH_LEN_POS_V2) & MATCH_RUN_LEN_V2); + if (nMatchLen != MATCH_RUN_LEN_V2 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd && (pSrc + 10) <= pOutDataEnd) { + memcpy(pCurOutData, pSrc, 8); + memcpy(pCurOutData + 8, pSrc + 8, 2); + pCurOutData += (MIN_MATCH_SIZE_V2 + nMatchLen); + } + else { + nMatchLen += MIN_MATCH_SIZE_V2; + if (nMatchLen == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) { + if (lzsa_build_len_v2(&pInBlock, pInBlockEnd, &nCurNibbles, &nibbles, &nMatchLen)) + return -1; + if (nMatchLen == 0) + break; + } + + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + /* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */ + + if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) { + const unsigned char *pCopySrc = pSrc; + unsigned char *pCopyDst = pCurOutData; + const unsigned char *pCopyEndDst = pCurOutData + nMatchLen; + + do { + memcpy(pCopyDst, pCopySrc, 16); + pCopySrc += 16; + pCopyDst += 16; + } while (pCopyDst < pCopyEndDst); + + pCurOutData += nMatchLen; + } + else { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + } + else { + return -1; + } + } + else { + return -1; + } + } + } + else { + return -1; + } + } + } + + return (int)(pCurOutData - (pOutData + nOutDataOffset)); +} diff --git a/loader/tools/lzsa/src/expand_block_v2.h b/loader/tools/lzsa/src/expand_block_v2.h new file mode 100644 index 0000000..f612f7a --- /dev/null +++ b/loader/tools/lzsa/src/expand_block_v2.h @@ -0,0 +1,49 @@ +/* + * expand_block_v2.h - LZSA2 block decompressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_BLOCK_V2_H +#define _EXPAND_BLOCK_V2_H + +/** + * Decompress one LZSA2 data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block_v2(const unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize); + +#endif /* _EXPAND_BLOCK_V2_H */ diff --git a/loader/tools/lzsa/src/expand_context.c b/loader/tools/lzsa/src/expand_context.c new file mode 100644 index 0000000..b02620d --- /dev/null +++ b/loader/tools/lzsa/src/expand_context.c @@ -0,0 +1,76 @@ +/* + * expand_context.h - decompressor context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "expand_context.h" +#include "expand_block_v1.h" +#include "expand_block_v2.h" +#include "lib.h" + +/** + * Decompress one data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * @param nFormatVersion version of format to use (1-2) + * @param nFlags compression flags (LZSA_FLAG_xxx) + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block(unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize, const int nFormatVersion, const int nFlags) { + int nDecompressedSize; + + if (nFlags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInBlock, nBlockSize); + } + + if (nFormatVersion == 1) + nDecompressedSize = lzsa_decompressor_expand_block_v1(pInBlock, nBlockSize, pOutData, nOutDataOffset, nBlockMaxSize); + else if (nFormatVersion == 2) + nDecompressedSize = lzsa_decompressor_expand_block_v2(pInBlock, nBlockSize, pOutData, nOutDataOffset, nBlockMaxSize); + else + nDecompressedSize = -1; + + if (nDecompressedSize != -1 && (nFlags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData + nOutDataOffset, nDecompressedSize); + } + + if (nFlags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInBlock, nBlockSize); + } + + return nDecompressedSize; +} diff --git a/loader/tools/lzsa/src/expand_context.h b/loader/tools/lzsa/src/expand_context.h new file mode 100644 index 0000000..698e039 --- /dev/null +++ b/loader/tools/lzsa/src/expand_context.h @@ -0,0 +1,61 @@ +/* + * expand_context.h - decompressor context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_CONTEXT_H +#define _EXPAND_CONTEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Decompress one data block + * + * @param pInBlock pointer to compressed data + * @param nBlockSize size of compressed data, in bytes + * @param pOutData pointer to output decompression buffer (previously decompressed bytes + room for decompressing this block) + * @param nOutDataOffset starting index of where to store decompressed bytes in output buffer (and size of previously decompressed bytes) + * @param nBlockMaxSize total size of output decompression buffer, in bytes + * @param nFormatVersion version of format to use (1-2) + * @param nFlags compression flags (LZSA_FLAG_xxx) + * + * @return size of decompressed data in bytes, or -1 for error + */ +int lzsa_decompressor_expand_block(unsigned char *pInBlock, int nBlockSize, unsigned char *pOutData, int nOutDataOffset, int nBlockMaxSize, const int nFormatVersion, const int nFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_CONTEXT_H */ diff --git a/loader/tools/lzsa/src/expand_inmem.c b/loader/tools/lzsa/src/expand_inmem.c new file mode 100644 index 0000000..050bdbc --- /dev/null +++ b/loader/tools/lzsa/src/expand_inmem.c @@ -0,0 +1,163 @@ +/* + * expand_inmem.c - in-memory decompression implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "expand_inmem.h" +#include "lib.h" +#include "frame.h" + +#define BLOCK_SIZE 65536 + +/** + * Get maximum decompressed size of compressed data + * + * @param pFileData compressed data + * @param nFileSize compressed size in bytes + * + * @return maximum decompressed size + */ +size_t lzsa_get_max_decompressed_size_inmem(const unsigned char *pFileData, size_t nFileSize) { + const unsigned char *pCurFileData = pFileData; + const unsigned char *pEndFileData = pCurFileData + nFileSize; + int nFormatVersion = 0; + size_t nMaxDecompressedSize = 0; + const int nHeaderSize = lzsa_get_header_size(); + + /* Check header */ + if ((pCurFileData + nHeaderSize) > pEndFileData || + lzsa_decode_header(pCurFileData, nHeaderSize, &nFormatVersion) != 0) + return -1; + + pCurFileData += nHeaderSize; + + while (pCurFileData < pEndFileData) { + unsigned int nBlockDataSize = 0; + int nIsUncompressed = 0; + const int nFrameSize = lzsa_get_frame_size(); + + /* Decode frame header */ + if ((pCurFileData + nFrameSize) > pEndFileData || + lzsa_decode_frame(pCurFileData, nFrameSize, &nBlockDataSize, &nIsUncompressed) != 0) + return -1; + pCurFileData += nFrameSize; + + if (!nBlockDataSize) + break; + + /* Add one potentially full block to the decompressed size */ + nMaxDecompressedSize += BLOCK_SIZE; + + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + + pCurFileData += nBlockDataSize; + } + + return nMaxDecompressedSize; +} + +/** + * Decompress data in memory + * + * @param pFileData compressed data + * @param pOutBuffer buffer for decompressed data + * @param nFileSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param pFormatVersion pointer to format version, updated if this function is successful + * + * @return actual decompressed size, or -1 for error + */ +size_t lzsa_decompress_inmem(unsigned char *pFileData, unsigned char *pOutBuffer, size_t nFileSize, size_t nMaxOutBufferSize, const unsigned int nFlags, int *pFormatVersion) { + unsigned char *pCurFileData = pFileData; + const unsigned char *pEndFileData = pCurFileData + nFileSize; + unsigned char *pCurOutBuffer = pOutBuffer; + const unsigned char *pEndOutBuffer = pCurOutBuffer + nMaxOutBufferSize; + int nPreviousBlockSize; + const int nHeaderSize = lzsa_get_header_size(); + + if (nFlags & LZSA_FLAG_RAW_BLOCK) { + return (size_t)lzsa_decompressor_expand_block(pFileData, (int)nFileSize, pOutBuffer, 0, (int)nMaxOutBufferSize, *pFormatVersion, nFlags); + } + + /* Check header */ + if ((pCurFileData + nHeaderSize) > pEndFileData || + lzsa_decode_header(pCurFileData, nHeaderSize, pFormatVersion) != 0) + return -1; + + pCurFileData += nHeaderSize; + nPreviousBlockSize = 0; + + while (pCurFileData < pEndFileData) { + unsigned int nBlockDataSize = 0; + int nIsUncompressed = 0; + const int nFrameSize = lzsa_get_frame_size(); + + /* Decode frame header */ + if ((pCurFileData + nFrameSize) > pEndFileData || + lzsa_decode_frame(pCurFileData, nFrameSize, &nBlockDataSize, &nIsUncompressed) != 0) + return -1; + pCurFileData += nFrameSize; + + if (!nBlockDataSize) + break; + + if (!nIsUncompressed) { + int nDecompressedSize; + + /* Decompress block */ + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + + nDecompressedSize = lzsa_decompressor_expand_block(pCurFileData, nBlockDataSize, pCurOutBuffer - nPreviousBlockSize, nPreviousBlockSize, (int)(pEndOutBuffer - pCurOutBuffer + nPreviousBlockSize), *pFormatVersion, nFlags); + if (nDecompressedSize < 0) + return -1; + + pCurOutBuffer += nDecompressedSize; + nPreviousBlockSize = nDecompressedSize; + } + else { + /* Copy uncompressed block */ + if ((pCurFileData + nBlockDataSize) > pEndFileData) + return -1; + if ((pCurOutBuffer + nBlockDataSize) > pEndOutBuffer) + return -1; + memcpy(pCurOutBuffer, pCurFileData, nBlockDataSize); + pCurOutBuffer += nBlockDataSize; + } + + pCurFileData += nBlockDataSize; + } + + return (int)(pCurOutBuffer - pOutBuffer); +} diff --git a/loader/tools/lzsa/src/expand_inmem.h b/loader/tools/lzsa/src/expand_inmem.h new file mode 100644 index 0000000..b52ee68 --- /dev/null +++ b/loader/tools/lzsa/src/expand_inmem.h @@ -0,0 +1,70 @@ +/* + * expand_inmem.h - in-memory decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_INMEM_H +#define _EXPAND_INMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum decompressed size of compressed data + * + * @param pFileData compressed data + * @param nFileSize compressed size in bytes + * + * @return maximum decompressed size + */ +size_t lzsa_get_max_decompressed_size_inmem(const unsigned char *pFileData, size_t nFileSize); + +/** + * Decompress data in memory + * + * @param pFileData compressed data + * @param pOutBuffer buffer for decompressed data + * @param nFileSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param pFormatVersion pointer to format version, updated if this function is successful + * + * @return actual decompressed size, or -1 for error + */ +size_t lzsa_decompress_inmem(unsigned char *pFileData, unsigned char *pOutBuffer, size_t nFileSize, size_t nMaxOutBufferSize, const unsigned int nFlags, int *pFormatVersion); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_INMEM_H */ diff --git a/loader/tools/lzsa/src/expand_streaming.c b/loader/tools/lzsa/src/expand_streaming.c new file mode 100644 index 0000000..b241c68 --- /dev/null +++ b/loader/tools/lzsa/src/expand_streaming.c @@ -0,0 +1,236 @@ +/* + * expand_streaming.c - streaming decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + + +#include +#include +#include "expand_streaming.h" +#include "format.h" +#include "frame.h" +#include "lib.h" + +/*-------------- File API -------------- */ + +/** + * Decompress file + * + * @param pszInFilename name of input(compressed) file to decompress + * @param pszOutFilename name of output(decompressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize) { + lzsa_stream_t inStream, outStream; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + lzsa_status_t nStatus; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + return LZSA_ERROR_SRC; + } + + if (lzsa_filestream_open(&outStream, pszOutFilename, "wb") < 0) { + inStream.close(&inStream); + return LZSA_ERROR_DST; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + if (nStatus) { + outStream.close(&outStream); + inStream.close(&inStream); + return nStatus; + } + + nStatus = lzsa_decompress_stream(&inStream, &outStream, pDictionaryData, nDictionaryDataSize, nFlags, nFormatVersion, pOriginalSize, pCompressedSize); + + lzsa_dictionary_free(&pDictionaryData); + outStream.close(&outStream); + inStream.close(&inStream); + + return nStatus; +} + +/*-------------- Streaming API -------------- */ + +/** + * Decompress stream + * + * @param pInStream input(compressed) stream to decompress + * @param pOutStream output(decompressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize) { + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + unsigned char cFrameData[16]; + unsigned char *pInBlock; + unsigned char *pOutData; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + const int nHeaderSize = lzsa_get_header_size(); + + memset(cFrameData, 0, 16); + if (pInStream->read(pInStream, cFrameData, nHeaderSize) != nHeaderSize) { + return LZSA_ERROR_SRC; + } + + if (lzsa_decode_header(cFrameData, nHeaderSize, &nFormatVersion) < 0) { + return LZSA_ERROR_FORMAT; + } + + nCompressedSize += (long long)nHeaderSize; + } + + pInBlock = (unsigned char*)malloc(BLOCK_SIZE); + if (!pInBlock) { + return LZSA_ERROR_MEMORY; + } + + pOutData = (unsigned char*)malloc(BLOCK_SIZE * 2); + if (!pOutData) { + free(pInBlock); + pInBlock = NULL; + + return LZSA_ERROR_MEMORY; + } + + int nDecompressionError = 0; + int nPrevDecompressedSize = 0; + int nNumBlocks = 0; + + while (!pInStream->eof(pInStream) && !nDecompressionError) { + unsigned int nBlockSize = 0; + int nIsUncompressed = 0; + + if (nPrevDecompressedSize != 0) { + memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pOutData + BLOCK_SIZE, nPrevDecompressedSize); + } + else if (nDictionaryDataSize && pDictionaryData) { + nPrevDecompressedSize = nDictionaryDataSize; + memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pDictionaryData, nPrevDecompressedSize); + } + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + const int nFrameSize = lzsa_get_frame_size(); + + memset(cFrameData, 0, 16); + if (pInStream->read(pInStream, cFrameData, nFrameSize) == nFrameSize) { + if (lzsa_decode_frame(cFrameData, nFrameSize, &nBlockSize, &nIsUncompressed) < 0) { + nDecompressionError = LZSA_ERROR_FORMAT; + nBlockSize = 0; + } + + nCompressedSize += (long long)nFrameSize; + } + else { + nDecompressionError = LZSA_ERROR_SRC; + nBlockSize = 0; + } + } + else { + if (!nNumBlocks) + nBlockSize = BLOCK_SIZE; + else + nBlockSize = 0; + } + + if (nBlockSize != 0) { + int nDecompressedSize = 0; + + if ((int)nBlockSize > BLOCK_SIZE) { + nDecompressionError = LZSA_ERROR_FORMAT; + break; + } + size_t nReadBytes = pInStream->read(pInStream, pInBlock, nBlockSize); + if (nFlags & LZSA_FLAG_RAW_BLOCK) { + nBlockSize = (unsigned int)nReadBytes; + } + + if (nReadBytes == nBlockSize) { + nCompressedSize += (long long)nReadBytes; + + if (nIsUncompressed) { + memcpy(pOutData + BLOCK_SIZE, pInBlock, nBlockSize); + nDecompressedSize = nBlockSize; + } + else { + nDecompressedSize = lzsa_decompressor_expand_block(pInBlock, nBlockSize, pOutData, BLOCK_SIZE, BLOCK_SIZE, nFormatVersion, nFlags); + if (nDecompressedSize < 0) { + nDecompressionError = LZSA_ERROR_DECOMPRESSION; + break; + } + } + + if (nDecompressedSize != 0) { + nOriginalSize += (long long)nDecompressedSize; + + if (pOutStream->write(pOutStream, pOutData + BLOCK_SIZE, nDecompressedSize) != nDecompressedSize) + nDecompressionError = LZSA_ERROR_DST; + nPrevDecompressedSize = nDecompressedSize; + nDecompressedSize = 0; + } + } + else { + break; + } + + nNumBlocks++; + } + else { + break; + } + } + + free(pOutData); + pOutData = NULL; + + free(pInBlock); + pInBlock = NULL; + + *pOriginalSize = nOriginalSize; + *pCompressedSize = nCompressedSize; + return nDecompressionError; +} + diff --git a/loader/tools/lzsa/src/expand_streaming.h b/loader/tools/lzsa/src/expand_streaming.h new file mode 100644 index 0000000..30dd88d --- /dev/null +++ b/loader/tools/lzsa/src/expand_streaming.h @@ -0,0 +1,86 @@ +/* + * expand_streaming.h - streaming decompression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _EXPAND_STREAMING_H +#define _EXPAND_STREAMING_H + +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef enum _lzsa_status_t lzsa_status_t; + +/*-------------- File API -------------- */ + +/** + * Decompress file + * + * @param pszInFilename name of input(compressed) file to decompress + * @param pszOutFilename name of output(decompressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize); + +/*-------------- Streaming API -------------- */ + +/** + * Decompress stream + * + * @param pInStream input(compressed) stream to decompress + * @param pOutStream output(decompressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_RAW_BLOCK to decompress a raw block, or 0) + * @param nFormatVersion default version of format to use (1-2). This is used when decompressing a raw block, otherwise the version is extracted from the source file + * @param pOriginalSize pointer to returned output(decompressed) size, updated when this function is successful + * @param pCompressedSize pointer to returned input(compressed) size, updated when this function is successful + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_decompress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, const unsigned int nFlags, int nFormatVersion, + long long *pOriginalSize, long long *pCompressedSize); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_STREAMING_H */ diff --git a/loader/tools/lzsa/src/format.h b/loader/tools/lzsa/src/format.h new file mode 100755 index 0000000..ce135f1 --- /dev/null +++ b/loader/tools/lzsa/src/format.h @@ -0,0 +1,54 @@ +/* + * format.h - byte stream format definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define MIN_OFFSET 1 +#define MAX_OFFSET 0xffff + +#define MAX_VARLEN 0xffff + +#define BLOCK_SIZE 65536 + +#define MIN_MATCH_SIZE_V1 3 +#define LITERALS_RUN_LEN_V1 7 +#define MATCH_RUN_LEN_V1 15 + +#define MIN_MATCH_SIZE_V2 2 +#define LITERALS_RUN_LEN_V2 3 +#define MATCH_RUN_LEN_V2 7 + +#define LITERALS_LEN_POS_V2 0 +#define MATCH_LEN_POS_V2 2 + +#endif /* _FORMAT_H */ diff --git a/loader/tools/lzsa/src/frame.c b/loader/tools/lzsa/src/frame.c new file mode 100644 index 0000000..755de9c --- /dev/null +++ b/loader/tools/lzsa/src/frame.c @@ -0,0 +1,236 @@ +/* + * frame.c - frame implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +/* + * + * Changes to implement lzsa2 as depacker/packer for the bitfire demo-framework + * 2021 by Tobias Bindhammer aka Bitbreaker/Performers^Oxyron tobias.bindhammer@uni-ulm.de + * + */ + +#include +#include +#include +#include "frame.h" + +#define LZSA_ID_0 0x7b +#define LZSA_ID_1 0x9e + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void) { + return 3; +} + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void) { + return 3; +} + +/** + * Encode file address big-endian + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nAddress address to encode + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_addr_be(unsigned char *pFrameData, const int nMaxFrameDataSize, int nAddress) { + if (nMaxFrameDataSize >= 2) { + pFrameData[0] = (nAddress >> 8) & 0xff; + pFrameData[1] = nAddress & 0xff; + return 2; + } + else { + return -1; + } +} + +/** + * Encode file address little-endian + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nAddress address to encode + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_addr_le(unsigned char *pFrameData, const int nMaxFrameDataSize, int nAddress) { + if (nMaxFrameDataSize >= 2) { + pFrameData[0] = nAddress & 0xff; + pFrameData[1] = (nAddress >> 8) & 0xff; + return 2; + } + else { + return -1; + } +} + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize) { + if (nMaxFrameDataSize >= 0) { + return 0; + } + else { + return -1; + } +} + +/* XXX TODO code below can be removed */ + + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = ((nBlockDataSize >> 16) & 0x7f) | 0x80; /* Uncompressed block */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion) { + if (nMaxFrameDataSize >= 3 && (nFormatVersion == 1 || nFormatVersion == 2)) { + pFrameData[0] = LZSA_ID_0; /* Magic number */ + pFrameData[1] = LZSA_ID_1; + pFrameData[2] = (nFormatVersion == 2) ? 0x20 : 0; /* Format version 1 */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = (nBlockDataSize >> 16) & 0x7f; + + return 3; + } + else { + return -1; + } +} + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion) { + if (nFrameDataSize != 3 || + pFrameData[0] != LZSA_ID_0 || + pFrameData[1] != LZSA_ID_1 || + (pFrameData[2] & 0x1f) != 0 || + ((pFrameData[2] & 0xe0) != 0x00 && (pFrameData[2] & 0xe0) != 0x20)) { + return -1; + } + else { + *nFormatVersion = (pFrameData[2] & 0xe0) ? 2 : 1; + return 0; + } +} + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed) { + if (nFrameDataSize == 3) { + *nBlockSize = ((unsigned int)pFrameData[0]) | + (((unsigned int)pFrameData[1]) << 8) | + (((unsigned int)pFrameData[2]) << 16); + + *nIsUncompressed = ((*nBlockSize & 0x800000) != 0) ? 1 : 0; + *nBlockSize &= 0x7fffff; + return 0; + } + else { + return -1; + } +} diff --git a/loader/tools/lzsa/src/frame.h b/loader/tools/lzsa/src/frame.h new file mode 100644 index 0000000..6fa192c --- /dev/null +++ b/loader/tools/lzsa/src/frame.h @@ -0,0 +1,144 @@ +/* + * frame.h - frame definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _FRAME_H +#define _FRAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void); + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void); + +/** + * Encode file address big-endian + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nAddress address to encode + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_addr_be(unsigned char *pFrameData, const int nMaxFrameDataSize, int nAddress); + +/** + * Encode file address little-endian + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nAddress address to encode + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_addr_le(unsigned char *pFrameData, const int nMaxFrameDataSize, int nAddress); + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize, int nFormatVersion); + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize); + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize, int *nFormatVersion); + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRAME_H */ diff --git a/loader/tools/lzsa/src/lib.h b/loader/tools/lzsa/src/lib.h new file mode 100755 index 0000000..2520b13 --- /dev/null +++ b/loader/tools/lzsa/src/lib.h @@ -0,0 +1,95 @@ +/* + * lib.h - LZSA library definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _LIB_H +#define _LIB_H + +#include "stream.h" +#include "dictionary.h" +#include "frame.h" +#include "format.h" +#include "shrink_context.h" +#include "shrink_streaming.h" +#include "shrink_inmem.h" +#include "expand_context.h" +#include "expand_streaming.h" +#include "expand_inmem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** High level status for compression and decompression */ +typedef enum _lzsa_status_t { + LZSA_OK = 0, /**< Success */ + LZSA_ERROR_SRC, /**< Error reading input */ + LZSA_ERROR_DST, /**< Error reading output */ + LZSA_ERROR_DICTIONARY, /**< Error reading dictionary */ + LZSA_ERROR_MEMORY, /**< Out of memory */ + + /* Compression-specific status codes */ + LZSA_ERROR_COMPRESSION, /**< Internal compression error */ + LZSA_ERROR_RAW_TOOLARGE, /**< Input is too large to be compressed to a raw block */ + LZSA_ERROR_RAW_UNCOMPRESSED, /**< Input is incompressible and raw blocks don't support uncompressed data */ + + /* Decompression-specific status codes */ + LZSA_ERROR_FORMAT, /**< Invalid input format or magic number when decompressing */ + LZSA_ERROR_DECOMPRESSION /**< Internal decompression error */ +} lzsa_status_t; + +/* Compression flags */ +#define LZSA_FLAG_FAVOR_RATIO (1<<0) /**< 1 to compress with the best ratio, 0 to trade some compression ratio for extra decompression speed */ +#define LZSA_FLAG_RAW_BLOCK (1<<1) /**< 1 to emit raw block */ +#define LZSA_FLAG_RAW_BACKWARD (1<<2) /**< 1 to compress or decompress raw block backward */ + +/** + * Reverse bytes in the specified buffer + * + * @param pBuffer pointer to buffer whose contents are to be reversed + * @param nBufferSize size of buffer in bytes + */ +static inline void lzsa_reverse_buffer(unsigned char *pBuffer, const int nBufferSize) { + int nMidPoint = nBufferSize / 2; + int i, j; + + for (i = 0, j = nBufferSize - 1; i < nMidPoint; i++, j--) { + unsigned char c = pBuffer[i]; + pBuffer[i] = pBuffer[j]; + pBuffer[j] = c; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* _LIB_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/.gitignore b/loader/tools/lzsa/src/libdivsufsort/.gitignore new file mode 100644 index 0000000..a0a0666 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/.gitignore @@ -0,0 +1,32 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# CMake files/directories +build/ diff --git a/loader/tools/lzsa/src/libdivsufsort/CHANGELOG.md b/loader/tools/lzsa/src/libdivsufsort/CHANGELOG.md new file mode 100644 index 0000000..fe9d004 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CHANGELOG.md @@ -0,0 +1,21 @@ +# libdivsufsort Change Log + +See full changelog at: https://github.com/y-256/libdivsufsort/commits + +## [2.0.1] - 2010-11-11 +### Fixed +* Wrong variable used in `divbwt` function +* Enclose some string variables with double quotation marks in include/CMakeLists.txt +* Fix typo in include/CMakeLists.txt + +## 2.0.0 - 2008-08-23 +### Changed +* Switch the build system to [CMake](http://www.cmake.org/) +* Improve the performance of the suffix-sorting algorithm + +### Added +* OpenMP support +* 64-bit version of divsufsort + +[Unreleased]: https://github.com/y-256/libdivsufsort/compare/2.0.1...HEAD +[2.0.1]: https://github.com/y-256/libdivsufsort/compare/2.0.0...2.0.1 diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeLists.txt b/loader/tools/lzsa/src/libdivsufsort/CMakeLists.txt new file mode 100644 index 0000000..7859943 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeLists.txt @@ -0,0 +1,99 @@ +### cmake file for building libdivsufsort Package ### +cmake_minimum_required(VERSION 2.4.4) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +include(AppendCompilerFlags) + +## Project information ## +project(libdivsufsort C) +set(PROJECT_VENDOR "Yuta Mori") +set(PROJECT_CONTACT "yuta.256@gmail.com") +set(PROJECT_URL "https://github.com/y-256/libdivsufsort") +set(PROJECT_DESCRIPTION "A lightweight suffix sorting library") +include(VERSION.cmake) + +## CPack configuration ## +set(CPACK_GENERATOR "TGZ;TBZ2;ZIP") +set(CPACK_SOURCE_GENERATOR "TGZ;TBZ2;ZIP") +include(ProjectCPack) + +## Project options ## +option(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) +option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_DIVSUFSORT64 "Build libdivsufsort64" OFF) +option(USE_OPENMP "Use OpenMP for parallelization" OFF) +option(WITH_LFS "Enable Large File Support" ON) + +## Installation directories ## +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32 or 64)") + +set(CMAKE_INSTALL_RUNTIMEDIR "" CACHE PATH "Specify the output directory for dll runtimes (default is bin)") +if(NOT CMAKE_INSTALL_RUNTIMEDIR) + set(CMAKE_INSTALL_RUNTIMEDIR "${CMAKE_INSTALL_PREFIX}/bin") +endif(NOT CMAKE_INSTALL_RUNTIMEDIR) + +set(CMAKE_INSTALL_LIBDIR "" CACHE PATH "Specify the output directory for libraries (default is lib)") +if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") +endif(NOT CMAKE_INSTALL_LIBDIR) + +set(CMAKE_INSTALL_INCLUDEDIR "" CACHE PATH "Specify the output directory for header files (default is include)") +if(NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") +endif(NOT CMAKE_INSTALL_INCLUDEDIR) + +set(CMAKE_INSTALL_PKGCONFIGDIR "" CACHE PATH "Specify the output directory for pkgconfig files (default is lib/pkgconfig)") +if(NOT CMAKE_INSTALL_PKGCONFIGDIR) + set(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif(NOT CMAKE_INSTALL_PKGCONFIGDIR) + +## Build type ## +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_VERBOSE_MAKEFILE ON) +endif(NOT CMAKE_BUILD_TYPE) + +## Compiler options ## +if(MSVC) + append_c_compiler_flags("/W4" "VC" CMAKE_C_FLAGS) + append_c_compiler_flags("/Oi;/Ot;/Ox;/Oy" "VC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("/openmp" "VC" CMAKE_C_FLAGS) + endif(USE_OPENMP) +elseif(BORLAND) + append_c_compiler_flags("-w" "BCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-Oi;-Og;-Os;-Ov;-Ox" "BCC" CMAKE_C_FLAGS_RELEASE) +else(MSVC) + if(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "GCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "GCC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp" "GCC" CMAKE_C_FLAGS) + endif(USE_OPENMP) + else(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "UNKNOWN" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "UNKNOWN" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp;-openmp;-omp" "UNKNOWN" CMAKE_C_FLAGS) + endif(USE_OPENMP) + endif(CMAKE_COMPILER_IS_GNUCC) +endif(MSVC) + +## Add definitions ## +add_definitions(-DHAVE_CONFIG_H=1 -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + +## Add subdirectories ## +add_subdirectory(pkgconfig) +add_subdirectory(include) +add_subdirectory(lib) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif(BUILD_EXAMPLES) + +## Add 'uninstall' target ## +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake") diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake new file mode 100644 index 0000000..58d3f99 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake @@ -0,0 +1,38 @@ +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +macro(append_c_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_c_compiler_flags) + +macro(append_cxx_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_cxx_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_cxx_compiler_flags) diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake new file mode 100644 index 0000000..e2b0099 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/CheckLFS.cmake @@ -0,0 +1,109 @@ +## Checks for large file support ## +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) + +macro(check_lfs _isenable) + set(LFS_OFF_T "") + set(LFS_FOPEN "") + set(LFS_FSEEK "") + set(LFS_FTELL "") + set(LFS_PRID "") + + if(${_isenable}) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + -D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 + -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + + check_include_file("sys/types.h" HAVE_SYS_TYPES_H) + check_include_file("inttypes.h" HAVE_INTTYPES_H) + check_include_file("stddef.h" HAVE_STDDEF_H) + check_include_file("stdint.h" HAVE_STDINT_H) + + # LFS type1: 8 <= sizeof(off_t), fseeko, ftello + check_type_size("off_t" SIZEOF_OFF_T) + if(SIZEOF_OFF_T GREATER 7) + check_symbol_exists("fseeko" "stdio.h" HAVE_FSEEKO) + check_symbol_exists("ftello" "stdio.h" HAVE_FTELLO) + if(HAVE_FSEEKO AND HAVE_FTELLO) + set(LFS_OFF_T "off_t") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseeko") + set(LFS_FTELL "ftello") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FSEEKO AND HAVE_FTELLO) + endif(SIZEOF_OFF_T GREATER 7) + + # LFS type2: 8 <= sizeof(off64_t), fopen64, fseeko64, ftello64 + if(NOT LFS_OFF_T) + check_type_size("off64_t" SIZEOF_OFF64_T) + if(SIZEOF_OFF64_T GREATER 7) + check_symbol_exists("fopen64" "stdio.h" HAVE_FOPEN64) + check_symbol_exists("fseeko64" "stdio.h" HAVE_FSEEKO64) + check_symbol_exists("ftello64" "stdio.h" HAVE_FTELLO64) + if(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + set(LFS_OFF_T "off64_t") + set(LFS_FOPEN "fopen64") + set(LFS_FSEEK "fseeko64") + set(LFS_FTELL "ftello64") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + endif(SIZEOF_OFF64_T GREATER 7) + endif(NOT LFS_OFF_T) + + # LFS type3: 8 <= sizeof(__int64), _fseeki64, _ftelli64 + if(NOT LFS_OFF_T) + check_type_size("__int64" SIZEOF___INT64) + if(SIZEOF___INT64 GREATER 7) + check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) + check_symbol_exists("_ftelli64" "stdio.h" HAVE__FTELLI64) + if(HAVE__FSEEKI64 AND HAVE__FTELLI64) + set(LFS_OFF_T "__int64") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "_fseeki64") + set(LFS_FTELL "_ftelli64") + set(LFS_PRID "\"I64d\"") + endif(HAVE__FSEEKI64 AND HAVE__FTELLI64) + endif(SIZEOF___INT64 GREATER 7) + endif(NOT LFS_OFF_T) + + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") + endif(${_isenable}) + + if(NOT LFS_OFF_T) + ## not found + set(LFS_OFF_T "long") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseek") + set(LFS_FTELL "ftell") + set(LFS_PRID "\"ld\"") + endif(NOT LFS_OFF_T) + +endmacro(check_lfs) diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake new file mode 100644 index 0000000..7c105f9 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/ProjectCPack.cmake @@ -0,0 +1,38 @@ +# If the cmake version includes cpack, use it +IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") + SET(CPACK_PACKAGE_VENDOR "${PROJECT_VENDOR}") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") + SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") + SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +# SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_FULL}") + + IF(NOT DEFINED CPACK_SYSTEM_NAME) + SET(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF(NOT DEFINED CPACK_SYSTEM_NAME) + + IF(${CPACK_SYSTEM_NAME} MATCHES Windows) + IF(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR}) + ELSE(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR}) + ENDIF(CMAKE_CL_64) + ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows) + + IF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") + ENDIF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + + SET(CPACK_PACKAGE_CONTACT "${PROJECT_CONTACT}") + IF(UNIX) + SET(CPACK_STRIP_FILES "") + SET(CPACK_SOURCE_STRIP_FILES "") +# SET(CPACK_PACKAGE_EXECUTABLES "ccmake" "CMake") + ENDIF(UNIX) + SET(CPACK_SOURCE_IGNORE_FILES "/CVS/" "/build/" "/\\\\.build/" "/\\\\.svn/" "~$") + # include CPack model once all variables are set + INCLUDE(CPack) +ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") diff --git a/loader/tools/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in new file mode 100644 index 0000000..8366a83 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in @@ -0,0 +1,36 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") + +SET(NUM 0) +FOREACH(file ${files}) + IF(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - found") + SET(UNINSTALL_CHECK_${NUM} 1) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - not found") + SET(UNINSTALL_CHECK_${NUM} 0) + ENDIF(EXISTS "$ENV{DESTDIR}${file}") + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +SET(NUM 0) +FOREACH(file ${files}) + IF(${UNINSTALL_CHECK_${NUM}}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ENDIF(${UNINSTALL_CHECK_${NUM}}) + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +FILE(REMOVE "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") diff --git a/loader/tools/lzsa/src/libdivsufsort/LICENSE b/loader/tools/lzsa/src/libdivsufsort/LICENSE new file mode 100644 index 0000000..249efa4 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2003 Yuta Mori All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/loader/tools/lzsa/src/libdivsufsort/README.md b/loader/tools/lzsa/src/libdivsufsort/README.md new file mode 100644 index 0000000..381a188 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/README.md @@ -0,0 +1,140 @@ +# libdivsufsort + +libdivsufsort is a software library that implements a lightweight suffix array construction algorithm. + +## News +* 2015-03-21: The project has moved from [Google Code](http://code.google.com/p/libdivsufsort/) to [GitHub](https://github.com/y-256/libdivsufsort) + +## Introduction +This library provides a simple and an efficient C API to construct a suffix array and a Burrows-Wheeler transformed string from a given string over a constant-size alphabet. +The algorithm runs in O(n log n) worst-case time using only 5n+O(1) bytes of memory space, where n is the length of +the string. + +## Build requirements +* An ANSI C Compiler (e.g. GNU GCC) +* [CMake](http://www.cmake.org/ "CMake") version 2.4.2 or newer +* CMake-supported build tool + +## Building on GNU/Linux +1. Get the source code from GitHub. You can either + * use git to clone the repository + ``` + git clone https://github.com/y-256/libdivsufsort.git + ``` + * or download a [zip file](../../archive/master.zip) directly +2. Create a `build` directory in the package source directory. +```shell +$ cd libdivsufsort +$ mkdir build +$ cd build +``` +3. Configure the package for your system. +If you want to install to a different location, change the -DCMAKE_INSTALL_PREFIX option. +```shell +$ cmake -DCMAKE_BUILD_TYPE="Release" \ +-DCMAKE_INSTALL_PREFIX="/usr/local" .. +``` +4. Compile the package. +```shell +$ make +``` +5. (Optional) Install the library and header files. +```shell +$ sudo make install +``` + +## API +```c +/* Data types */ +typedef int32_t saint_t; +typedef int32_t saidx_t; +typedef uint8_t sauchar_t; + +/* + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array or suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +saint_t +divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n); + +/* + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); +``` + +## Example Usage +```c +#include +#include +#include + +#include + +int main() { + // intput data + char *Text = "abracadabra"; + int n = strlen(Text); + int i, j; + + // allocate + int *SA = (int *)malloc(n * sizeof(int)); + + // sort + divsufsort((unsigned char *)Text, SA, n); + + // output + for(i = 0; i < n; ++i) { + printf("SA[%2d] = %2d: ", i, SA[i]); + for(j = SA[i]; j < n; ++j) { + printf("%c", Text[j]); + } + printf("$\n"); + } + + // deallocate + free(SA); + + return 0; +} +``` +See the [examples](examples) directory for a few other examples. + +## Benchmarks +See [Benchmarks](https://github.com/y-256/libdivsufsort/blob/wiki/SACA_Benchmarks.md) page for details. + +## License +libdivsufsort is released under the [MIT license](LICENSE "MIT license"). +> The MIT License (MIT) +> +> Copyright (c) 2003 Yuta Mori All rights reserved. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +## Author +* Yuta Mori diff --git a/loader/tools/lzsa/src/libdivsufsort/VERSION.cmake b/loader/tools/lzsa/src/libdivsufsort/VERSION.cmake new file mode 100644 index 0000000..3f11ac1 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/VERSION.cmake @@ -0,0 +1,23 @@ +set(PROJECT_VERSION_MAJOR "2") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "2") +set(PROJECT_VERSION_EXTRA "-1") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(PROJECT_VERSION_FULL "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_EXTRA}") + +set(LIBRARY_VERSION "3.0.1") +set(LIBRARY_SOVERSION "3") + +## Git revision number ## +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + execute_process(COMMAND git describe --tags HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_DESCRIBE_TAGS ERROR_QUIET) + if(GIT_DESCRIBE_TAGS) + string(REGEX REPLACE "^v(.*)" "\\1" GIT_REVISION "${GIT_DESCRIBE_TAGS}") + string(STRIP "${GIT_REVISION}" GIT_REVISION) + if(GIT_REVISION) + set(PROJECT_VERSION_FULL "${GIT_REVISION}") + endif(GIT_REVISION) + endif(GIT_DESCRIBE_TAGS) +endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/CMakeLists.txt b/loader/tools/lzsa/src/libdivsufsort/examples/CMakeLists.txt new file mode 100644 index 0000000..e801c81 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +## Add definitions ## +add_definitions(-D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64) + +## Targets ## +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") +link_directories("${CMAKE_CURRENT_BINARY_DIR}/../lib") +foreach(src suftest mksary sasearch bwt unbwt) + add_executable(${src} ${src}.c) + target_link_libraries(${src} divsufsort) +endforeach(src) diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/bwt.c b/loader/tools/lzsa/src/libdivsufsort/examples/bwt.c new file mode 100644 index 0000000..5a362d0 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/bwt.c @@ -0,0 +1,220 @@ +/* + * bwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +write_int(FILE *fp, saidx_t n) { + unsigned char c[4]; + c[0] = (unsigned char)((n >> 0) & 0xff), c[1] = (unsigned char)((n >> 8) & 0xff), + c[2] = (unsigned char)((n >> 16) & 0xff), c[3] = (unsigned char)((n >> 24) & 0xff); + return fwrite(c, sizeof(unsigned char), 4, fp); +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "bwt, a burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s [-b num] INFILE OUTFILE\n", progname); + fprintf(stderr, " -b num set block size to num MiB [1..512] (default: 32)\n\n"); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start,finish; + saint_t i, blocksize = 32, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if((argc != 3) && (argc != 5)) { print_help(argv[0], EXIT_FAILURE); } + i = 1; + if(argc == 5) { + if(strcmp(argv[i], "-b") != 0) { print_help(argv[0], EXIT_FAILURE); } + blocksize = atoi(argv[i + 1]); + if(blocksize < 0) { blocksize = 1; } + else if(512 < blocksize) { blocksize = 512; } + i += 2; + } + blocksize <<= 20; + + /* Open a file for reading. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[i], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[i], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + i += 1; + + /* Open a file for writing. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[i], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[i], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x20000000L < n) { n = 0x20000000L; } + if((blocksize == 0) || (n < blocksize)) { blocksize = (saidx_t)n; } + } else if(blocksize == 0) { blocksize = 32 << 20; } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + SA = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Write the blocksize. */ + if(write_int(ofp, blocksize) != 4) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + + fprintf(stderr, " BWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; 0 < (m = fread(T, sizeof(sauchar_t), blocksize, fp)); n += m) { + /* Burrows-Wheeler Transform. */ + pidx = divbwt(T, T, SA, m); + if(pidx < 0) { + fprintf(stderr, "%s (bw_transform): %s.\n", + argv[0], + (pidx == -1) ? "Invalid arguments" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write the bwted data. */ + if((write_int(ofp, pidx) != 4) || + (fwrite(T, sizeof(sauchar_t), m, ofp) != m)) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/mksary.c b/loader/tools/lzsa/src/libdivsufsort/examples/mksary.c new file mode 100644 index 0000000..b48177c --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/mksary.c @@ -0,0 +1,193 @@ +/* + * mksary.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "mksary, a simple suffix array builder, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Write the suffix array. */ + if(fwrite(SA, sizeof(saidx_t), (size_t)n, ofp) != (size_t)n) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/sasearch.c b/loader/tools/lzsa/src/libdivsufsort/examples/sasearch.c new file mode 100644 index 0000000..7e5ca4f --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/sasearch.c @@ -0,0 +1,165 @@ +/* + * sasearch.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "sasearch, a simple SA-based full-text search tool, version %s\n", + divsufsort_version()); + fprintf(stderr, "usage: %s PATTERN FILE SAFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *P; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t Psize; + saidx_t i, size, left; + + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 4) { print_help(argv[0], EXIT_FAILURE); } + + P = argv[1]; + Psize = strlen(P); + + /* Open a file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[2], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[2], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Open the SA file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[3], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[3], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Read n * sizeof(saidx_t) bytes of data. */ + if(fread(SA, sizeof(saidx_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Search and print */ + size = sa_search(T, (saidx_t)n, + (const sauchar_t *)P, (saidx_t)Psize, + SA, (saidx_t)n, &left); + for(i = 0; i < size; ++i) { + fprintf(stdout, "%" PRIdSAIDX_T "\n", SA[left + i]); + } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/suftest.c b/loader/tools/lzsa/src/libdivsufsort/examples/suftest.c new file mode 100644 index 0000000..71892ac --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/suftest.c @@ -0,0 +1,164 @@ +/* + * suftest.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "suftest, a suffixsort tester, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s FILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *fname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 1; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 2) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose = 0; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[1]); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Check the suffix array. */ + if(sufcheck(T, SA, (saidx_t)n, 1) != 0) { exit(EXIT_FAILURE); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/loader/tools/lzsa/src/libdivsufsort/examples/unbwt.c b/loader/tools/lzsa/src/libdivsufsort/examples/unbwt.c new file mode 100644 index 0000000..c0f19e9 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/examples/unbwt.c @@ -0,0 +1,207 @@ +/* + * unbwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include +# include +#endif +#include +#include +#include "lfs.h" + + +static +size_t +read_int(FILE *fp, saidx_t *n) { + unsigned char c[4]; + size_t m = fread(c, sizeof(unsigned char), 4, fp); + if(m == 4) { + *n = (c[0] << 0) | (c[1] << 8) | + (c[2] << 16) | (c[3] << 24); + } + return m; +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "unbwt, an inverse burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *A; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start, finish; + saint_t err, blocksize, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Read the blocksize. */ + if(read_int(fp, &blocksize) != 4) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + A = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (A == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "UnBWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; (m = read_int(fp, &pidx)) != 0; n += m) { + /* Read blocksize bytes of data. */ + if((m != 4) || ((m = fread(T, sizeof(sauchar_t), blocksize, fp)) == 0)) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Inverse Burrows-Wheeler Transform. */ + if((err = inverse_bw_transform(T, T, A, m, pidx)) != 0) { + fprintf(stderr, "%s (reverseBWT): %s.\n", + argv[0], + (err == -1) ? "Invalid data" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write m bytes of data. */ + if(fwrite(T, sizeof(sauchar_t), m, ofp) != m) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(A); + free(T); + + return 0; +} diff --git a/loader/tools/lzsa/src/libdivsufsort/include/CMakeLists.txt b/loader/tools/lzsa/src/libdivsufsort/include/CMakeLists.txt new file mode 100644 index 0000000..37781cc --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/CMakeLists.txt @@ -0,0 +1,162 @@ +include(CheckIncludeFiles) +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) +include(CheckFunctionKeywords) +include(CheckLFS) + +## Checks for header files ## +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_include_file("memory.h" HAVE_MEMORY_H) +check_include_file("stddef.h" HAVE_STDDEF_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stdlib.h" HAVE_STDLIB_H) +check_include_file("string.h" HAVE_STRING_H) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) +if(HAVE_INTTYPES_H) + set(INCFILE "#include ") +elseif(HAVE_STDINT_H) + set(INCFILE "#include ") +else(HAVE_INTTYPES_H) + set(INCFILE "") +endif(HAVE_INTTYPES_H) + +## create configuration files from .cmake file ## +if(BUILD_EXAMPLES) + ## Checks for WinIO ## + if(WIN32) + check_include_file("io.h" HAVE_IO_H) + check_include_file("fcntl.h" HAVE_FCNTL_H) + check_symbol_exists("_setmode" "io.h;fcntl.h" HAVE__SETMODE) + if(NOT HAVE__SETMODE) + check_symbol_exists("setmode" "io.h;fcntl.h" HAVE_SETMODE) + endif(NOT HAVE__SETMODE) + check_symbol_exists("_fileno" "stdio.h" HAVE__FILENO) + check_symbol_exists("fopen_s" "stdio.h" HAVE_FOPEN_S) + check_symbol_exists("_O_BINARY" "fcntl.h" HAVE__O_BINARY) + endif(WIN32) + + ## Checks for large file support ## + check_lfs(WITH_LFS) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lfs.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lfs.h" @ONLY) +endif(BUILD_EXAMPLES) + +## generate config.h ## +check_function_keywords("inline;__inline;__inline__;__declspec(dllexport);__declspec(dllimport)") +if(HAVE_INLINE) + set(INLINE "inline") +elseif(HAVE___INLINE) + set(INLINE "__inline") +elseif(HAVE___INLINE__) + set(INLINE "__inline__") +else(HAVE_INLINE) + set(INLINE "") +endif(HAVE_INLINE) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +## Checks for types ## +# sauchar_t (8bit) +check_type_size("uint8_t" UINT8_T) +if(HAVE_UINT8_T) + set(SAUCHAR_TYPE "uint8_t") +else(HAVE_UINT8_T) + check_type_size("unsigned char" SIZEOF_UNSIGNED_CHAR) + if("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + set(SAUCHAR_TYPE "unsigned char") + else("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + message(FATAL_ERROR "Cannot find unsigned 8-bit integer type") + endif("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") +endif(HAVE_UINT8_T) +# saint_t (32bit) +check_type_size("int32_t" INT32_T) +if(HAVE_INT32_T) + set(SAINT32_TYPE "int32_t") + check_symbol_exists("PRId32" "inttypes.h" HAVE_PRID32) + if(HAVE_PRID32) + set(SAINT32_PRId "PRId32") + else(HAVE_PRID32) + set(SAINT32_PRId "\"d\"") + endif(HAVE_PRID32) +else(HAVE_INT32_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("short" SIZEOF_SHORT) + check_type_size("__int32" SIZEOF___INT32) + if("${SIZEOF_INT}" STREQUAL "4") + set(SAINT32_TYPE "int") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "4") + set(SAINT32_TYPE "long") + set(SAINT32_PRId "\"ld\"") + elseif("${SIZEOF_SHORT}" STREQUAL "4") + set(SAINT32_TYPE "short") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF___INT32}" STREQUAL "4") + set(SAINT32_TYPE "__int32") + set(SAINT32_PRId "\"d\"") + else("${SIZEOF_INT}" STREQUAL "4") + message(FATAL_ERROR "Cannot find 32-bit integer type") + endif("${SIZEOF_INT}" STREQUAL "4") +endif(HAVE_INT32_T) +# saint64_t (64bit) +if(BUILD_DIVSUFSORT64) + check_type_size("int64_t" INT64_T) + if(HAVE_INT64_T) + set(SAINT64_TYPE "int64_t") + check_symbol_exists("PRId64" "inttypes.h" HAVE_PRID64) + if(HAVE_PRID64) + set(SAINT64_PRId "PRId64") + else(HAVE_PRID64) + set(SAINT64_PRId "\"lld\"") + endif(HAVE_PRID64) + else(HAVE_INT64_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("long long" SIZEOF_LONG_LONG) + check_type_size("__int64" SIZEOF___INT64) + if("${SIZEOF_INT}" STREQUAL "8") + set(SAINT64_TYPE "int") + set(SAINT64_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long") + set(SAINT64_PRId "\"ld\"") + elseif("${SIZEOF_LONG_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long long") + set(SAINT64_PRId "\"lld\"") + elseif("${SIZEOF___INT64}" STREQUAL "8") + set(SAINT64_TYPE "__int64") + set(SAINT64_PRId "\"I64d\"") + else("${SIZEOF_INT}" STREQUAL "8") + message(SEND_ERROR "Cannot find 64-bit integer type") + set(BUILD_DIVSUFSORT64 OFF) + endif("${SIZEOF_INT}" STREQUAL "8") + endif(HAVE_INT64_T) +endif(BUILD_DIVSUFSORT64) + +## generate divsufsort.h ## +set(DIVSUFSORT_IMPORT "") +set(DIVSUFSORT_EXPORT "") +if(BUILD_SHARED_LIBS) + if(HAVE___DECLSPEC_DLLIMPORT_) + set(DIVSUFSORT_IMPORT "__declspec(dllimport)") + endif(HAVE___DECLSPEC_DLLIMPORT_) + if(HAVE___DECLSPEC_DLLEXPORT_) + set(DIVSUFSORT_EXPORT "__declspec(dllexport)") + endif(HAVE___DECLSPEC_DLLEXPORT_) +endif(BUILD_SHARED_LIBS) +set(W64BIT "") +set(SAINDEX_TYPE "${SAINT32_TYPE}") +set(SAINDEX_PRId "${SAINT32_PRId}") +set(SAINT_PRId "${SAINT32_PRId}") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + set(SAINDEX_TYPE "${SAINT64_TYPE}") + set(SAINDEX_PRId "${SAINT64_PRId}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/lzsa/src/libdivsufsort/include/config.h.cmake b/loader/tools/lzsa/src/libdivsufsort/include/config.h.cmake new file mode 100644 index 0000000..6a1cf47 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/config.h.cmake @@ -0,0 +1,81 @@ +/* + * config.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CONFIG_H +#define _CONFIG_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Define to the version of this package. **/ +#cmakedefine PROJECT_VERSION_FULL "${PROJECT_VERSION_FULL}" + +/** Define to 1 if you have the header files. **/ +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_STDLIB_H 1 +#cmakedefine HAVE_STRING_H 1 +#cmakedefine HAVE_STRINGS_H 1 +#cmakedefine HAVE_MEMORY_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/** for WinIO **/ +#cmakedefine HAVE_IO_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE__SETMODE 1 +#cmakedefine HAVE_SETMODE 1 +#cmakedefine HAVE__FILENO 1 +#cmakedefine HAVE_FOPEN_S 1 +#cmakedefine HAVE__O_BINARY 1 +#ifndef HAVE__SETMODE +# if HAVE_SETMODE +# define _setmode setmode +# define HAVE__SETMODE 1 +# endif +# if HAVE__SETMODE && !HAVE__O_BINARY +# define _O_BINARY 0 +# define HAVE__O_BINARY 1 +# endif +#endif + +/** for inline **/ +#ifndef INLINE +# define INLINE @INLINE@ +#endif + +/** for VC++ warning **/ +#ifdef _MSC_VER +#pragma warning(disable: 4127) +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _CONFIG_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h new file mode 100755 index 0000000..7ebb412 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h @@ -0,0 +1,189 @@ +/* + * divsufsort.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DIVSUFSORT_API + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef unsigned char sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef int saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX_T +#define SAIDX_T +typedef int saidx_t; +#endif /* SAIDX_T */ +#ifndef PRIdSAIDX_T +#define PRIdSAIDX_T "d" +#endif + +/*- divsufsort context */ +typedef struct _divsufsort_ctx_t { + saidx_t *bucket_A; + saidx_t *bucket_B; +} divsufsort_ctx_t; + +/*- Prototypes -*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx); + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx); + +/** + * Constructs the suffix array of a given string. + * @param ctx suffix array context + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n); + +#if 0 +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *SA /* can NULL */, + saidx_t n, saidx_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *A /* can NULL */, + saidx_t n, saidx_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *left); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h.cmake b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h.cmake new file mode 100644 index 0000000..bcaba7c --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort.h.cmake @@ -0,0 +1,180 @@ +/* + * divsufsort@W64BIT@.h for libdivsufsort@W64BIT@ + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT@W64BIT@_H +#define _DIVSUFSORT@W64BIT@_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +@INCFILE@ + +#ifndef DIVSUFSORT_API +# ifdef DIVSUFSORT_BUILD_DLL +# define DIVSUFSORT_API @DIVSUFSORT_EXPORT@ +# else +# define DIVSUFSORT_API @DIVSUFSORT_IMPORT@ +# endif +#endif + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef @SAUCHAR_TYPE@ sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef @SAINT32_TYPE@ saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX@W64BIT@_T +#define SAIDX@W64BIT@_T +typedef @SAINDEX_TYPE@ saidx@W64BIT@_t; +#endif /* SAIDX@W64BIT@_T */ +#ifndef PRIdSAINT_T +#define PRIdSAINT_T @SAINT_PRId@ +#endif /* PRIdSAINT_T */ +#ifndef PRIdSAIDX@W64BIT@_T +#define PRIdSAIDX@W64BIT@_T @SAINDEX_PRId@ +#endif /* PRIdSAIDX@W64BIT@_T */ + + +/*- Prototypes -*/ + +/** + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +divsufsort@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t *SA, saidx@W64BIT@_t n); + +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +divbwt@W64BIT@(const sauchar_t *T, sauchar_t *U, saidx@W64BIT@_t *A, saidx@W64BIT@_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort@W64BIT@_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *SA /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *A /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck@W64BIT@(const sauchar_t *T, const saidx@W64BIT@_t *SA, saidx@W64BIT@_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_search@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const sauchar_t *P, saidx@W64BIT@_t Psize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saidx@W64BIT@_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_simplesearch@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saint_t c, saidx@W64BIT@_t *left); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT@W64BIT@_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_config.h b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_config.h new file mode 100644 index 0000000..4054a8a --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_config.h @@ -0,0 +1,9 @@ +#define HAVE_STRING_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDINT_H 1 +#define INLINE inline + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ diff --git a/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_private.h b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_private.h new file mode 100644 index 0000000..b4d97ad --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/divsufsort_private.h @@ -0,0 +1,205 @@ +/* + * divsufsort_private.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_PRIVATE_H +#define _DIVSUFSORT_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "divsufsort_config.h" +#include +#include +#if HAVE_STRING_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_MEMORY_H +# include +#endif +#if HAVE_STDDEF_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if defined(BUILD_DIVSUFSORT64) +# include "divsufsort64.h" +# ifndef SAIDX_T +# define SAIDX_T +# define saidx_t saidx64_t +# endif /* SAIDX_T */ +# ifndef PRIdSAIDX_T +# define PRIdSAIDX_T PRIdSAIDX64_T +# endif /* PRIdSAIDX_T */ +# define divsufsort divsufsort64 +# define divbwt divbwt64 +# define divsufsort_version divsufsort64_version +# define bw_transform bw_transform64 +# define inverse_bw_transform inverse_bw_transform64 +# define sufcheck sufcheck64 +# define sa_search sa_search64 +# define sa_simplesearch sa_simplesearch64 +# define sssort sssort64 +# define trsort trsort64 +#else +# include "divsufsort.h" +#endif + + +/*- Constants -*/ +#if !defined(UINT8_MAX) +# define UINT8_MAX (255) +#endif /* UINT8_MAX */ +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (UINT8_MAX + 1) +#endif +/* for divsufsort.c */ +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +/* for sssort.c */ +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) +# define SS_MISORT_STACKSIZE (96) +# else +# define SS_MISORT_STACKSIZE (64) +# endif +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#if defined(BUILD_DIVSUFSORT64) +# define SS_SMERGE_STACKSIZE (64) +#else +# define SS_SMERGE_STACKSIZE (32) +#endif +/* for trsort.c */ +#define TR_INSERTIONSORT_THRESHOLD (8) +#if defined(BUILD_DIVSUFSORT64) +# define TR_STACKSIZE (96) +#else +# define TR_STACKSIZE (64) +#endif + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +/* for divsufsort.c */ +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Prototypes -*/ +/* sssort.c */ +void +sssort(const sauchar_t *Td, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix); +/* trsort.c */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/include/lfs.h.cmake b/loader/tools/lzsa/src/libdivsufsort/include/lfs.h.cmake new file mode 100644 index 0000000..d5b84a8 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/include/lfs.h.cmake @@ -0,0 +1,56 @@ +/* + * lfs.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LFS_H +#define _LFS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __STRICT_ANSI__ +# define LFS_OFF_T @LFS_OFF_T@ +# define LFS_FOPEN @LFS_FOPEN@ +# define LFS_FTELL @LFS_FTELL@ +# define LFS_FSEEK @LFS_FSEEK@ +# define LFS_PRId @LFS_PRID@ +#else +# define LFS_OFF_T long +# define LFS_FOPEN fopen +# define LFS_FTELL ftell +# define LFS_FSEEK fseek +# define LFS_PRId "ld" +#endif +#ifndef PRIdOFF_T +# define PRIdOFF_T LFS_PRId +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _LFS_H */ diff --git a/loader/tools/lzsa/src/libdivsufsort/lib/CMakeLists.txt b/loader/tools/lzsa/src/libdivsufsort/lib/CMakeLists.txt new file mode 100644 index 0000000..abc90e6 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/lib/CMakeLists.txt @@ -0,0 +1,31 @@ +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") + +set(divsufsort_SRCS divsufsort.c sssort.c trsort.c utils.c) + +## libdivsufsort ## +add_library(divsufsort ${divsufsort_SRCS}) +install(TARGETS divsufsort + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +set_target_properties(divsufsort PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") + +## libdivsufsort64 ## +if(BUILD_DIVSUFSORT64) + add_library(divsufsort64 ${divsufsort_SRCS}) + install(TARGETS divsufsort64 + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties(divsufsort64 PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + COMPILE_FLAGS "-DBUILD_DIVSUFSORT64" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort.c b/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort.c new file mode 100755 index 0000000..50631ac --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort.c @@ -0,0 +1,431 @@ +/* + * divsufsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" +#ifdef _OPENMP +# include +#endif + + +/*- Private Functions -*/ + +/* Sorts suffixes of type B*. */ +static +saidx_t +sort_typeBstar(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n) { + saidx_t *PAb, *ISAb, *buf; +#ifdef _OPENMP + saidx_t *curbuf; + saidx_t l; +#endif + saidx_t i, j, k, t, m, bufsize; + saint_t c0, c1; +#ifdef _OPENMP + saint_t d0, d1; + int tmp; +#endif + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef _OPENMP + tmp = omp_get_max_threads(); + buf = SA + m, bufsize = (n - (2 * m)) / tmp; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) + { + tmp = omp_get_thread_num(); + curbuf = buf + tmp * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +#if 0 +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +saidx_t +construct_BWT(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k, *orig; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((saidx_t)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx) { + ctx->bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + ctx->bucket_B = NULL; + + if (ctx->bucket_A) { + ctx->bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + if (ctx->bucket_B) + return 0; + } + + divsufsort_destroy(ctx); + return -1; +} + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx) { + if (ctx->bucket_B) { + free(ctx->bucket_B); + ctx->bucket_B = NULL; + } + + if (ctx->bucket_A) { + free(ctx->bucket_A); + ctx->bucket_A = NULL; + } +} + +/*- Function -*/ + +saint_t +divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n) { + saidx_t m; + saint_t err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + /* Suffixsort. */ + if((ctx->bucket_A != NULL) && (ctx->bucket_B != NULL)) { + m = sort_typeBstar(T, SA, ctx->bucket_A, ctx->bucket_B, n); + construct_SA(T, SA, ctx->bucket_A, ctx->bucket_B, n, m); + } else { + err = -2; + } + + return err; +} + +#if 0 +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { + saidx_t *B; + saidx_t *bucket_A, *bucket_B; + saidx_t m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } + bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n); + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} + +const char * +divsufsort_version(void) { + return PROJECT_VERSION_FULL; +} +#endif diff --git a/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort_utils.c b/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort_utils.c new file mode 100644 index 0000000..f7cbc0d --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/lib/divsufsort_utils.c @@ -0,0 +1,383 @@ +/* + * utils.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Function -*/ + +#if 0 +/* Binary search for inverse bwt. */ +static +saidx_t +binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { + saidx_t half, i; + for(i = 0, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + if(A[i + half] < value) { + i += half + 1; + half -= (size & 1) ^ 1; + } + } + return i; +} + + +/*- Functions -*/ + +/* Burrows-Wheeler transform. */ +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, + saidx_t n, saidx_t *idx) { + saidx_t *A, i, j, p, t; + saint_t c; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } + if(n <= 1) { + if(n == 1) { U[0] = T[0]; } + *idx = n; + return 0; + } + + if((A = SA) == NULL) { + i = divbwt(T, U, NULL, n); + if(0 <= i) { *idx = i; i = 0; } + return (saint_t)i; + } + + /* BW transform. */ + if(T == U) { + t = n; + for(i = 0, j = 0; i < n; ++i) { + p = t - 1; + t = A[i]; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + j++; + } else { + *idx = i; + } + } + p = t - 1; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + } else { + *idx = i; + } + } else { + U[0] = T[n - 1]; + for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } + *idx = i + 1; + for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } + } + + if(SA == NULL) { + /* Deallocate memory. */ + free(A); + } + + return 0; +} + +/* Inverse Burrows-Wheeler transform. */ +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, + saidx_t n, saidx_t idx) { + saidx_t C[ALPHABET_SIZE]; + sauchar_t D[ALPHABET_SIZE]; + saidx_t *B; + saidx_t i, p; + saint_t c, d; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || + (n < idx) || ((0 < n) && (idx == 0))) { + return -1; + } + if(n <= 1) { return 0; } + + if((B = A) == NULL) { + /* Allocate n*sizeof(saidx_t) bytes of memory. */ + if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } + } + + /* Inverse BW transform. */ + for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { + p = C[c]; + if(0 < p) { + C[c] = i; + D[d++] = (sauchar_t)c; + i += p; + } + } + for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } + for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } + for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } + for(i = 0, p = idx; i < n; ++i) { + U[i] = D[binarysearch_lower(C, d, p)]; + p = B[p - 1]; + } + + if(A == NULL) { + /* Deallocate memory. */ + free(B); + } + + return 0; +} + +/* Checks the suffix array SA of the string T. */ +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, + saidx_t n, saint_t verbose) { + saidx_t C[ALPHABET_SIZE]; + saidx_t i, p, q, t; + saint_t c; + + if(verbose) { fprintf(stderr, "sufcheck: "); } + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { + if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } + return -1; + } + if(n == 0) { + if(verbose) { fprintf(stderr, "Done.\n"); } + return 0; + } + + /* check range: [0..n-1] */ + for(i = 0; i < n; ++i) { + if((SA[i] < 0) || (n <= SA[i])) { + if(verbose) { + fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + n - 1, i, SA[i]); + } + return -2; + } + } + + /* check first characters. */ + for(i = 1; i < n; ++i) { + if(T[SA[i - 1]] > T[SA[i]]) { + if(verbose) { + fprintf(stderr, "Suffixes in wrong order.\n" + " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" + " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", + i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); + } + return -3; + } + } + + /* check suffixes. */ + for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { + t = C[i]; + C[i] = p; + p += t; + } + + q = C[T[n - 1]]; + C[T[n - 1]] += 1; + for(i = 0; i < n; ++i) { + p = SA[i]; + if(0 < p) { + c = T[--p]; + t = C[c]; + } else { + c = T[p = n - 1]; + t = q; + } + if((t < 0) || (p != SA[t])) { + if(verbose) { + fprintf(stderr, "Suffix in wrong position.\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + t, (0 <= t) ? SA[t] : -1, i, SA[i]); + } + return -4; + } + if(t != q) { + ++C[c]; + if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } + } + } + + if(1 <= verbose) { fprintf(stderr, "Done.\n"); } + return 0; +} + + +static +int +_compare(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + saidx_t suf, saidx_t *match) { + saidx_t i, j; + saint_t r; + for(i = suf + *match, j = *match, r = 0; + (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } + *match = j; + return (r == 0) ? -(j != Psize) : r; +} + +/* Search for the pattern P in the string T. */ +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t match, lmatch, rmatch; + saidx_t llmatch, lrmatch, rlmatch, rrmatch; + saidx_t i, j, k; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (P == NULL) || (SA == NULL) || + (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } + + for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + match = MIN(lmatch, rmatch); + r = _compare(T, Tsize, P, Psize, SA[i + half], &match); + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + lmatch = match; + } else if(r > 0) { + rmatch = match; + } else { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + lmatch = MIN(llmatch, lrmatch); + r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + llmatch = lmatch; + } else { + lrmatch = lmatch; + } + } + + /* right part */ + for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + rmatch = MIN(rlmatch, rrmatch); + r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + rlmatch = rmatch; + } else { + rrmatch = rmatch; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} + +/* Search for the character c in the string T. */ +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t i, j, k, p; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + + for(i = j = k = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + p = SA[i + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + } else if(r == 0) { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + p = SA[j + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + } + } + + /* right part */ + for(half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + p = SA[k + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} +#endif diff --git a/loader/tools/lzsa/src/libdivsufsort/lib/sssort.c b/loader/tools/lzsa/src/libdivsufsort/lib/sssort.c new file mode 100644 index 0000000..4a18fd2 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/lib/sssort.c @@ -0,0 +1,815 @@ +/* + * sssort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +saint_t +ss_ilg(saidx_t n) { +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +# else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +# endif +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const saint_t sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +saidx_t +ss_isqrt(saidx_t x) { + saidx_t y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +saint_t +ss_compare(const sauchar_t *T, + const saidx_t *p1, const saidx_t *p2, + saidx_t depth) { + const sauchar_t *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *i, *j; + saidx_t t; + saint_t r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const sauchar_t *Td, const saidx_t *PA, + saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saint_t c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +ss_median3(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +ss_median5(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +saidx_t * +ss_partition(const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *a, *b; + saidx_t t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; + const sauchar_t *Td; + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t s, t; + saint_t ssize; + saint_t limit; + saint_t v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { + saidx_t t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { + saidx_t *a, *b, t; + saidx_t l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t depth) { + const saidx_t *p; + saidx_t *a, *b; + saidx_t len, half; + saint_t q, r; + saint_t x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + const saidx_t *p1, *p2; + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + saint_t x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t bufsize, saidx_t depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; + saidx_t *l, *r, *lm, *rm; + saidx_t m, len, half; + saint_t ssize; + saint_t check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Substring sort */ +void +sssort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix) { + saidx_t *a; +#if SS_BLOCKSIZE != 0 + saidx_t *b, *middle, *curbuf; + saidx_t j, k, curbufsize, limit; +#endif + saidx_t i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} diff --git a/loader/tools/lzsa/src/libdivsufsort/lib/trsort.c b/loader/tools/lzsa/src/libdivsufsort/lib/trsort.c new file mode 100644 index 0000000..6fe3e67 --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/lib/trsort.c @@ -0,0 +1,586 @@ +/* + * trsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +static INLINE +saint_t +tr_ilg(saidx_t n) { +#if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +#else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *a, *b; + saidx_t t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saidx_t c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +tr_median5(const saidx_t *ISAd, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + saidx_t chance; + saidx_t remain; + saidx_t incval; + saidx_t count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +saint_t +trbudget_check(trbudget_t *budget, saidx_t size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const saidx_t *ISAd, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t **pa, saidx_t **pb, saidx_t v) { + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t t, s; + saidx_t x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + saidx_t *c, *d, *e; + saidx_t s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + saidx_t *c, *d, *e; + saidx_t s, v; + saidx_t rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(saidx_t *ISA, const saidx_t *ISAd, + saidx_t *SA, saidx_t *first, saidx_t *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; + saidx_t *a, *b, *c; + saidx_t t; + saidx_t v, x = 0; + saidx_t incr = ISAd - ISA; + saint_t limit, next; + saint_t ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Tandem repeat sort */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { + saidx_t *ISAd; + saidx_t *first, *last; + trbudget_t budget; + saidx_t t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} diff --git a/loader/tools/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt b/loader/tools/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt new file mode 100644 index 0000000..ee7063c --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/pkgconfig/CMakeLists.txt @@ -0,0 +1,9 @@ +## generate libdivsufsort.pc ## +set(W64BIT "") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/loader/tools/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake b/loader/tools/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake new file mode 100644 index 0000000..6419d1e --- /dev/null +++ b/loader/tools/lzsa/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@@W64BIT@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION_FULL@ +URL: @PROJECT_URL@ +Libs: -L${libdir} -ldivsufsort@W64BIT@ +Cflags: -I${includedir} diff --git a/loader/tools/lzsa/src/lzsa.c b/loader/tools/lzsa/src/lzsa.c new file mode 100755 index 0000000..3f6e357 --- /dev/null +++ b/loader/tools/lzsa/src/lzsa.c @@ -0,0 +1,1109 @@ +/* + * lzsa.c - command line compression utility for the LZSA format + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif +#include "lib.h" + +#define OPT_VERBOSE 1 +#define OPT_RAW 2 +#define OPT_FAVOR_RATIO 4 +#define OPT_RAW_BACKWARD 8 +#define OPT_STATS 16 + +#define TOOL_VERSION "1.3.6" + +/*---------------------------------------------------------------------------*/ + +#ifdef _WIN32 +LARGE_INTEGER hpc_frequency; +BOOL hpc_available = FALSE; +#endif + +static void do_init_time() { +#ifdef _WIN32 + hpc_frequency.QuadPart = 0; + hpc_available = QueryPerformanceFrequency(&hpc_frequency); +#endif +} + +static long long do_get_time() { + long long nTime; + +#ifdef _WIN32 + if (hpc_available) { + LARGE_INTEGER nCurTime; + + /* Use HPC hardware for best precision */ + QueryPerformanceCounter(&nCurTime); + nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart); + } + else { + struct _timeb tb; + _ftime(&tb); + + nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL; + } +#else + struct timeval tm; + gettimeofday(&tm, NULL); + + nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec; +#endif + return nTime; +} + +/*---------------------------------------------------------------------------*/ + +static void compression_progress(long long nOriginalSize, long long nCompressedSize) { + if (nOriginalSize >= 1024 * 1024) { + fprintf(stdout, "\r%lld => %lld (%g %%) \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + fflush(stdout); + } +} + +static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const int nMinMatchSize, const int nFormatVersion) { + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + int nCommandCount = 0, nSafeDist = 0; + int nFlags; + lzsa_status_t nStatus; + lzsa_stats stats; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_compress_file(pszInFilename, pszOutFilename, pszDictionaryFilename, nFlags, nMinMatchSize, nFormatVersion, compression_progress, &nOriginalSize, &nCompressedSize, &nCommandCount, &nSafeDist, &stats); + + if ((nOptions & OPT_VERBOSE)) { + nEndTime = do_get_time(); + } + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error writing '%s'\n", pszOutFilename); break; + case LZSA_ERROR_DICTIONARY: fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_COMPRESSION: fprintf(stderr, "internal compression error\n"); break; + case LZSA_ERROR_RAW_TOOLARGE: fprintf(stderr, "error: raw blocks can only be used with files <= 64 Kb\n"); break; + case LZSA_ERROR_RAW_UNCOMPRESSED: fprintf(stderr, "error: incompressible data needs to be <= 64 Kb in raw blocks\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown compression error %d\n", nStatus); break; + } + + if (nStatus) + return 100; + + if ((nOptions & OPT_VERBOSE)) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %lld into %lld bytes ==> %g %%\n", + pszInFilename, fDelta, fSpeed, nCommandCount, (double)nOriginalSize / (double)nCommandCount, + nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + if (nOptions & OPT_RAW) { + fprintf(stdout, "Safe distance: %d (0x%X)\n", nSafeDist, nSafeDist); + } + } + + if (nOptions & OPT_STATS) { + if (stats.literals_divisor > 0) + fprintf(stdout, "Literals: min: %d avg: %d max: %d count: %d\n", stats.min_literals, stats.total_literals / stats.literals_divisor, stats.max_literals, stats.literals_divisor); + else + fprintf(stdout, "Literals: none\n"); + if (stats.match_divisor > 0) { + fprintf(stdout, "Offsets: min: %d avg: %d max: %d reps: %d count: %d\n", stats.min_offset, stats.total_offsets / stats.match_divisor, stats.max_offset, stats.num_rep_offsets, stats.match_divisor); + fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor); + } + else { + fprintf(stdout, "Offsets: none\n"); + fprintf(stdout, "Match lens: none\n"); + } + if (stats.rle1_divisor > 0) { + fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor); + } + else { + fprintf(stdout, "RLE1 lens: none\n"); + } + if (stats.rle2_divisor > 0) { + fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor); + } + else { + fprintf(stdout, "RLE2 lens: none\n"); + } + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + lzsa_status_t nStatus; + int nFlags; + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_decompress_file(pszInFilename, pszOutFilename, pszDictionaryFilename, nFlags, nFormatVersion, &nOriginalSize, &nCompressedSize); + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error writing '%s'\n", pszOutFilename); break; + case LZSA_ERROR_DICTIONARY: fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_DECOMPRESSION: fprintf(stderr, "internal decompression error\n"); break; + case LZSA_ERROR_FORMAT: fprintf(stderr, "invalid magic number or format version in input file\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown decompression error %d\n", nStatus); break; + } + + if (nStatus) { + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + else { + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; + } +} + +/*---------------------------------------------------------------------------*/ + +typedef struct { + FILE *f; + void *pCompareDataBuf; + size_t nCompareDataSize; +} compare_stream_t; + +void comparestream_close(lzsa_stream_t *stream) { + if (stream->obj) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + if (pCompareStream->pCompareDataBuf) { + free(pCompareStream->pCompareDataBuf); + pCompareStream->pCompareDataBuf = NULL; + } + + fclose(pCompareStream->f); + free(pCompareStream); + + stream->obj = NULL; + stream->read = NULL; + stream->write = NULL; + stream->eof = NULL; + stream->close = NULL; + } +} + +size_t comparestream_read(lzsa_stream_t *stream, void *ptr, size_t size) { + return 0; +} + +size_t comparestream_write(lzsa_stream_t *stream, void *ptr, size_t size) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + + if (!pCompareStream->pCompareDataBuf || pCompareStream->nCompareDataSize < size) { + pCompareStream->nCompareDataSize = size; + pCompareStream->pCompareDataBuf = realloc(pCompareStream->pCompareDataBuf, pCompareStream->nCompareDataSize); + if (!pCompareStream->pCompareDataBuf) + return 0; + } + + size_t nReadBytes = fread(pCompareStream->pCompareDataBuf, 1, size, pCompareStream->f); + if (nReadBytes != size) { + return 0; + } + + if (memcmp(ptr, pCompareStream->pCompareDataBuf, size)) { + return 0; + } + + return size; +} + +int comparestream_eof(lzsa_stream_t *stream) { + compare_stream_t *pCompareStream = (compare_stream_t *)stream->obj; + return feof(pCompareStream->f); +} + +int comparestream_open(lzsa_stream_t *stream, const char *pszCompareFilename, const char *pszMode) { + compare_stream_t *pCompareStream; + + pCompareStream = (compare_stream_t*)malloc(sizeof(compare_stream_t)); + if (!pCompareStream) + return -1; + + pCompareStream->pCompareDataBuf = NULL; + pCompareStream->nCompareDataSize = 0; + pCompareStream->f = (FILE*)fopen(pszCompareFilename, pszMode); + + if (pCompareStream->f) { + stream->obj = pCompareStream; + stream->read = comparestream_read; + stream->write = comparestream_write; + stream->eof = comparestream_eof; + stream->close = comparestream_close; + return 0; + } + else { + free(pCompareStream); + return -1; + } +} + +static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + lzsa_stream_t inStream, compareStream; + long long nStartTime = 0LL, nEndTime = 0LL; + long long nOriginalSize = 0LL; + long long nCompressedSize = 0LL; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + lzsa_status_t nStatus; + int nFlags; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + fprintf(stderr, "error opening compressed input file\n"); + return 100; + } + + if (comparestream_open(&compareStream, pszOutFilename, "rb") < 0) { + fprintf(stderr, "error opening original uncompressed file\n"); + inStream.close(&inStream); + return 100; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + if (nStatus) { + compareStream.close(&compareStream); + inStream.close(&inStream); + fprintf(stderr, "error reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nStatus = lzsa_decompress_stream(&inStream, &compareStream, pDictionaryData, nDictionaryDataSize, nFlags, nFormatVersion, &nOriginalSize, &nCompressedSize); + + switch (nStatus) { + case LZSA_ERROR_SRC: fprintf(stderr, "error reading '%s'\n", pszInFilename); break; + case LZSA_ERROR_DST: fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename); break; + case LZSA_ERROR_MEMORY: fprintf(stderr, "out of memory\n"); break; + case LZSA_ERROR_DECOMPRESSION: fprintf(stderr, "internal decompression error\n"); break; + case LZSA_ERROR_FORMAT: fprintf(stderr, "invalid magic number or format version in input file\n"); break; + case LZSA_OK: break; + default: fprintf(stderr, "unknown decompression error %d\n", nStatus); break; + } + + lzsa_dictionary_free(&pDictionaryData); + compareStream.close(&compareStream); + inStream.close(&inStream); + + if (nStatus) { + return 100; + } + else { + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; + } +} + +/*---------------------------------------------------------------------------*/ + +static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, int nMinMatchSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) { + size_t nIndex = 0; + int nMatchProbability = (int)(fMatchProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + pBuffer[nIndex++] = rand() % nNumLiteralValues; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) >= nMatchProbability) { + size_t nLiteralCount = rand() & 127; + if (nLiteralCount > (nBufferSize - nIndex)) + nLiteralCount = nBufferSize - nIndex; + + while (nLiteralCount--) + pBuffer[nIndex++] = rand() % nNumLiteralValues; + } + else { + size_t nMatchLength = nMinMatchSize + (rand() & 1023); + size_t nMatchOffset; + + if (nMatchLength > (nBufferSize - nIndex)) + nMatchLength = nBufferSize - nIndex; + if (nMatchLength > nIndex) + nMatchLength = nIndex; + + if (nMatchLength < nIndex) + nMatchOffset = rand() % (nIndex - nMatchLength); + else + nMatchOffset = 0; + + while (nMatchLength--) { + pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset]; + nIndex++; + } + } + } +} + +static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) { + size_t nIndex = 0; + int nXorProbability = (int)(fXorProbability * 1023.0f); + + srand(nSeed); + + if (nIndex >= nBufferSize) return; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) < nXorProbability) { + pBuffer[nIndex] ^= 0xff; + } + nIndex++; + } +} + +static int do_self_test(const unsigned int nOptions, const int nMinMatchSize, int nFormatVersion) { + unsigned char *pGeneratedData; + unsigned char *pCompressedData; + unsigned char *pTmpCompressedData; + unsigned char *pTmpDecompressedData; + size_t nGeneratedDataSize; + size_t nMaxCompressedDataSize; + unsigned int nSeed = 123; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pGeneratedData) { + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + nMaxCompressedDataSize = lzsa_get_max_compressed_size_inmem(4 * BLOCK_SIZE); + pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pCompressedData) { + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pTmpCompressedData) { + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pTmpDecompressedData) { + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + memset(pGeneratedData, 0, 4 * BLOCK_SIZE); + memset(pCompressedData, 0, nMaxCompressedDataSize); + memset(pTmpCompressedData, 0, nMaxCompressedDataSize); + + /* Test compressing with a too small buffer to do anything, expect to fail cleanly */ + for (i = 0; i < 12; i++) { + generate_compressible_data(pGeneratedData, i, nMinMatchSize, nSeed, 256, 0.5f); + lzsa_compress_inmem(pGeneratedData, pCompressedData, i, i, nFlags, nMinMatchSize, nFormatVersion); + } + + size_t nDataSizeStep = 128; + float fProbabilitySizeStep = 0.0005f; + + for (nGeneratedDataSize = 1024; nGeneratedDataSize <= ((size_t)((nOptions & OPT_RAW) ? BLOCK_SIZE : (4 * BLOCK_SIZE))); nGeneratedDataSize += nDataSizeStep) { + float fMatchProbability; + + fprintf(stdout, "size %zd", nGeneratedDataSize); + for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) { + int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 }; + float fXorProbability; + + fputc('.', stdout); + fflush(stdout); + + for (i = 0; i < 12; i++) { + /* Generate data to compress */ + generate_compressible_data(pGeneratedData, nGeneratedDataSize, nMinMatchSize, nSeed, nNumLiteralValues[i], fMatchProbability); + + /* Try to compress it, expected to succeed */ + size_t nActualCompressedSize = lzsa_compress_inmem(pGeneratedData, pCompressedData, nGeneratedDataSize, lzsa_get_max_compressed_size_inmem(nGeneratedDataSize), + nFlags, nMinMatchSize, nFormatVersion); + if (nActualCompressedSize == -1 || (int)nActualCompressedSize < (lzsa_get_header_size() + lzsa_get_frame_size() + lzsa_get_frame_size() /* footer */)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error compressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress it, expected to succeed */ + size_t nActualDecompressedSize; + int nDecFormatVersion = nFormatVersion; + nActualDecompressedSize = lzsa_decompress_inmem(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, nFlags, &nDecFormatVersion); + if (nActualDecompressedSize == -1) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error decompressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */ + for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) { + memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize); + xor_data(pTmpCompressedData + lzsa_get_header_size() + lzsa_get_frame_size(), nActualCompressedSize - lzsa_get_header_size() - lzsa_get_frame_size() - lzsa_get_frame_size() /* footer */, nSeed, fXorProbability); + nDecFormatVersion = nFormatVersion; + lzsa_decompress_inmem(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, nFlags, &nDecFormatVersion); + } + } + + nSeed++; + } + + fputc(10, stdout); + fflush(stdout); + + nDataSizeStep <<= 1; + if (nDataSizeStep > (128 * 4096)) + nDataSizeStep = 128 * 4096; + fProbabilitySizeStep *= 1.25; + if (fProbabilitySizeStep > (0.0005f * 4096)) + fProbabilitySizeStep = 0.0005f * 4096; + } + + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + + free(pTmpCompressedData); + pTmpCompressedData = NULL; + + free(pCompressedData); + pCompressedData = NULL; + + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stdout, "All tests passed.\n"); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const int nMinMatchSize, int nFormatVersion) { + size_t nFileSize, nMaxCompressedSize; + unsigned char *pFileData; + unsigned char *pCompressedData; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_FAVOR_RATIO) + nFlags |= LZSA_FLAG_FAVOR_RATIO; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + /* Allocate max compressed size */ + + nMaxCompressedSize = lzsa_get_max_compressed_size_inmem(nFileSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048); + if (!pCompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData + 1024, 0, nMaxCompressedSize); + + long long nBestCompTime = -1; + + size_t nActualCompressedSize = 0; + size_t nRightGuardPos = nMaxCompressedSize; + + for (i = 0; i < 5; i++) { + unsigned char nGuard = 0x33 + i; + int j; + + /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */ + memset(pCompressedData, nGuard, 1024); + memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024); + + long long t0 = do_get_time(); + nActualCompressedSize = lzsa_compress_inmem(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMinMatchSize, nFormatVersion); + long long t1 = do_get_time(); + if (nActualCompressedSize == -1) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "compression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestCompTime == -1 || nBestCompTime > nCurDecTime) + nBestCompTime = nCurDecTime; + + /* Check guard bytes before the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024); + return 100; + } + } + + /* Check guard bytes after the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j); + return 100; + } + } + + nRightGuardPos = nActualCompressedSize; + } + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole compressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out); + fclose(f_out); + } + } + + free(pCompressedData); + free(pFileData); + + fprintf(stdout, "compressed size: %zd bytes\n", nActualCompressedSize); + fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, int nFormatVersion) { + size_t nFileSize, nMaxDecompressedSize; + unsigned char *pFileData; + unsigned char *pDecompressedData; + int nFlags; + int i; + + nFlags = 0; + if (nOptions & OPT_RAW) + nFlags |= LZSA_FLAG_RAW_BLOCK; + if (nOptions & OPT_RAW_BACKWARD) + nFlags |= LZSA_FLAG_RAW_BACKWARD; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + + /* Allocate max decompressed size */ + + if (nOptions & OPT_RAW) + nMaxDecompressedSize = 65536; + else + nMaxDecompressedSize = lzsa_get_max_decompressed_size_inmem(pFileData, nFileSize); + if (nMaxDecompressedSize == -1) { + free(pFileData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize); + if (!pDecompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nMaxDecompressedSize); + + long long nBestDecTime = -1; + + size_t nActualDecompressedSize = 0; + for (i = 0; i < 50; i++) { + long long t0 = do_get_time(); + nActualDecompressedSize = lzsa_decompress_inmem(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, nFlags, &nFormatVersion); + long long t1 = do_get_time(); + if (nActualDecompressedSize == -1) { + free(pDecompressedData); + free(pFileData); + fprintf(stderr, "decompression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestDecTime == -1 || nBestDecTime > nCurDecTime) + nBestDecTime = nCurDecTime; + } + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole decompressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out); + fclose(f_out); + } + } + + free(pDecompressedData); + free(pFileData); + + fprintf(stdout, "format: LZSA%d\n", nFormatVersion); + fprintf(stdout, "decompressed size: %zd bytes\n", nActualDecompressedSize); + fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +int main(int argc, char **argv) { + int i; + const char *pszInFilename = NULL; + const char *pszOutFilename = NULL; + const char *pszDictionaryFilename = NULL; + int nArgsError = 0; + int nCommandDefined = 0; + int nVerifyCompression = 0; + int nMinMatchDefined = 0; + int nFormatVersionDefined = 0; + char cCommand = 'z'; + int nMinMatchSize = 0; + unsigned int nOptions = OPT_FAVOR_RATIO; + int nFormatVersion = 1; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'd'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-z")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'z'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-c")) { + if (!nVerifyCompression) { + nVerifyCompression = 1; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-cbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'B'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-dbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'b'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-test")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 't'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-D")) { + if (!pszDictionaryFilename && (i + 1) < argc) { + pszDictionaryFilename = argv[i + 1]; + i++; + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-D", 2)) { + if (!pszDictionaryFilename) { + pszDictionaryFilename = argv[i] + 2; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-m")) { + if (!nMinMatchDefined && (i + 1) < argc) { + char *pEnd = NULL; + nMinMatchSize = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nMinMatchSize >= 2 && nMinMatchSize <= 5)) { + i++; + nMinMatchDefined = 1; + nOptions &= (~OPT_FAVOR_RATIO); + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-m", 2)) { + if (!nMinMatchDefined) { + char *pEnd = NULL; + nMinMatchSize = (int)strtol(argv[i] + 2, &pEnd, 10); + if (pEnd && pEnd != (argv[i]+2) && (nMinMatchSize >= 2 && nMinMatchSize <= 5)) { + nMinMatchDefined = 1; + nOptions &= (~OPT_FAVOR_RATIO); + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "--prefer-ratio")) { + if (!nMinMatchDefined) { + nMinMatchSize = 0; + nMinMatchDefined = 1; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "--prefer-speed")) { + if (!nMinMatchDefined) { + nMinMatchSize = 3; + nOptions &= (~OPT_FAVOR_RATIO); + nMinMatchDefined = 1; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-f")) { + if (!nFormatVersionDefined && (i + 1) < argc) { + char *pEnd = NULL; + nFormatVersion = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nFormatVersion >= 1 && nFormatVersion <= 2)) { + i++; + nFormatVersionDefined = 1; + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-f", 2)) { + if (!nFormatVersionDefined) { + char *pEnd = NULL; + nFormatVersion = (int)strtol(argv[i] + 2, &pEnd, 10); + if (pEnd && pEnd != (argv[i] + 2) && (nFormatVersion >= 1 && nFormatVersion <= 2)) { + nFormatVersionDefined = 1; + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-v")) { + if ((nOptions & OPT_VERBOSE) == 0) { + nOptions |= OPT_VERBOSE; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-r")) { + if ((nOptions & OPT_RAW) == 0) { + nOptions |= OPT_RAW; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-b")) { + if ((nOptions & OPT_RAW_BACKWARD) == 0) { + nOptions |= OPT_RAW_BACKWARD; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-stats")) { + if ((nOptions & OPT_STATS) == 0) { + nOptions |= OPT_STATS; + } + else + nArgsError = 1; + } + else { + if (!pszInFilename) + pszInFilename = argv[i]; + else { + if (!pszOutFilename) + pszOutFilename = argv[i]; + else + nArgsError = 1; + } + } + } + + if (!nArgsError && (nOptions & OPT_RAW_BACKWARD) && !(nOptions & OPT_RAW)) { + fprintf(stderr, "error: -b (compress backwards) requires -r (raw block format)\n"); + return 100; + } + + if (!nArgsError && cCommand == 't') { + return do_self_test(nOptions, nMinMatchSize, nFormatVersion); + } + + if (nArgsError || !pszInFilename || !pszOutFilename) { + fprintf(stderr, "lzsa command-line tool v" TOOL_VERSION " by Emmanuel Marty and spke\n"); + fprintf(stderr, "usage: %s [-c] [-d] [-v] [-r] \n", argv[0]); + fprintf(stderr, " -c: check resulting stream after compressing\n"); + fprintf(stderr, " -d: decompress (default: compress)\n"); + fprintf(stderr, " -cbench: benchmark in-memory compression\n"); + fprintf(stderr, " -dbench: benchmark in-memory decompression\n"); + fprintf(stderr, " -test: run automated self-tests\n"); + fprintf(stderr, " -stats: show compressed data stats\n"); + fprintf(stderr, " -v: be verbose\n"); + fprintf(stderr, " -f : LZSA compression format (1-2)\n"); + fprintf(stderr, " -r: raw block format (max. 64 Kb files)\n"); + fprintf(stderr, " -b: compress backward (requires -r and a backward decompressor)\n"); + fprintf(stderr, " -D : use dictionary file\n"); + fprintf(stderr, " -m : minimum match size (3-5) (default: 3)\n"); + fprintf(stderr, " --prefer-ratio: favor compression ratio (default)\n"); + fprintf(stderr, " --prefer-speed: favor decompression speed (same as -m3)\n"); + return 100; + } + + do_init_time(); + + if (cCommand == 'z') { + int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMinMatchSize, nFormatVersion); + if (nResult == 0 && nVerifyCompression) { + return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } else { + return nResult; + } + } + else if (cCommand == 'd') { + return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } + else if (cCommand == 'B') { + return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMinMatchSize, nFormatVersion); + } + else if (cCommand == 'b') { + return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nFormatVersion); + } + else { + return 100; + } +} diff --git a/loader/tools/lzsa/src/matchfinder.c b/loader/tools/lzsa/src/matchfinder.c new file mode 100644 index 0000000..3de2cfa --- /dev/null +++ b/loader/tools/lzsa/src/matchfinder.c @@ -0,0 +1,361 @@ +/* + * matchfinder.c - LZ match finder implementation + * + * The following copying information applies to this specific source code file: + * + * Written in 2019 by Emmanuel Marty + * Portions written in 2014-2015 by Eric Biggers + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide via the Creative Commons Zero 1.0 Universal Public Domain + * Dedication (the "CC0"). + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the CC0 for more details. + * + * You should have received a copy of the CC0 along with this software; if not + * see . + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "matchfinder.h" +#include "format.h" +#include "lib.h" + +/** + * Hash index into TAG_BITS + * + * @param nIndex index value + * + * @return hash + */ +static inline int lzsa_get_index_tag(unsigned int nIndex) { + return (int)(((unsigned long long)nIndex * 11400714819323198485ULL) >> (64ULL - TAG_BITS)); +} + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int lzsa_build_suffix_array(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize) { + unsigned int *intervals = pCompressor->intervals; + + /* Build suffix array from input data */ + if (divsufsort_build_array(&pCompressor->divsufsort_context, pInWindow, (saidx_t*)intervals, nInWindowSize) != 0) { + return 100; + } + + int *PLCP = (int*)pCompressor->pos_data; /* Use temporarily */ + int *Phi = PLCP; + int nCurLen = 0; + int i, r; + + /* Compute the permuted LCP first (Kärkkäinen method) */ + Phi[intervals[0]] = -1; + for (i = 1; i < nInWindowSize; i++) + Phi[intervals[i]] = intervals[i - 1]; + for (i = 0; i < nInWindowSize; i++) { + if (Phi[i] == -1) { + PLCP[i] = 0; + continue; + } + int nMaxLen = (i > Phi[i]) ? (nInWindowSize - i) : (nInWindowSize - Phi[i]); + while (nCurLen < nMaxLen && pInWindow[i + nCurLen] == pInWindow[Phi[i] + nCurLen]) nCurLen++; + PLCP[i] = nCurLen; + if (nCurLen > 0) + nCurLen--; + } + + /* Rotate permuted LCP into the LCP. This has better cache locality than the direct Kasai LCP method. This also + * saves us from having to build the inverse suffix array index, as the LCP is calculated without it using this method, + * and the interval builder below doesn't need it either. */ + intervals[0] &= POS_MASK; + int nMinMatchSize = pCompressor->min_match_size; + + if (pCompressor->format_version >= 2) { + for (i = 1; i < nInWindowSize; i++) { + int nIndex = (int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < nMinMatchSize) + nLen = 0; + if (nLen > LCP_MAX) + nLen = LCP_MAX; + int nTaggedLen = 0; + if (nLen) + nTaggedLen = (nLen << TAG_BITS) | (lzsa_get_index_tag((unsigned int)nIndex) & ((1 << TAG_BITS) - 1)); + intervals[i] = ((unsigned int)nIndex) | (((unsigned int)nTaggedLen) << LCP_SHIFT); + } + } + else { + for (i = 1; i < nInWindowSize; i++) { + int nIndex = (int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < nMinMatchSize) + nLen = 0; + if (nLen > LCP_AND_TAG_MAX) + nLen = LCP_AND_TAG_MAX; + intervals[i] = ((unsigned int)nIndex) | (((unsigned int)nLen) << LCP_SHIFT); + } + } + + /** + * Build intervals for finding matches + * + * Methodology and code fragment taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + unsigned int * const SA_and_LCP = intervals; + unsigned int *pos_data = pCompressor->pos_data; + unsigned int next_interval_idx; + unsigned int *top = pCompressor->open_intervals; + unsigned int prev_pos = SA_and_LCP[0] & POS_MASK; + + *top = 0; + intervals[0] = 0; + next_interval_idx = 1; + + for (r = 1; r < nInWindowSize; r++) { + const unsigned int next_pos = SA_and_LCP[r] & POS_MASK; + const unsigned int next_lcp = SA_and_LCP[r] & LCP_MASK; + const unsigned int top_lcp = *top & LCP_MASK; + + if (next_lcp == top_lcp) { + /* Continuing the deepest open interval */ + pos_data[prev_pos] = *top; + } + else if (next_lcp > top_lcp) { + /* Opening a new interval */ + *++top = next_lcp | next_interval_idx++; + pos_data[prev_pos] = *top; + } + else { + /* Closing the deepest open interval */ + pos_data[prev_pos] = *top; + for (;;) { + const unsigned int closed_interval_idx = *top-- & POS_MASK; + const unsigned int superinterval_lcp = *top & LCP_MASK; + + if (next_lcp == superinterval_lcp) { + /* Continuing the superinterval */ + intervals[closed_interval_idx] = *top; + break; + } + else if (next_lcp > superinterval_lcp) { + /* Creating a new interval that is a + * superinterval of the one being + * closed, but still a subinterval of + * its superinterval */ + *++top = next_lcp | next_interval_idx++; + intervals[closed_interval_idx] = *top; + break; + } + else { + /* Also closing the superinterval */ + intervals[closed_interval_idx] = *top; + } + } + } + prev_pos = next_pos; + } + + /* Close any still-open intervals. */ + pos_data[prev_pos] = *top; + for (; top > pCompressor->open_intervals; top--) + intervals[*top & POS_MASK] = *(top - 1); + + /* Success */ + return 0; +} + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param nMaxMatches maximum number of matches to return (0 for none) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return number of matches + */ +int lzsa_find_matches_at(lzsa_compressor *pCompressor, const int nOffset, lzsa_match *pMatches, const int nMaxMatches, const int nInWindowSize) { + unsigned int *intervals = pCompressor->intervals; + unsigned int *pos_data = pCompressor->pos_data; + unsigned int ref; + unsigned int super_ref; + unsigned int match_pos; + lzsa_match *matchptr; + int nPrevOffset = 0; + + /** + * Find matches using intervals + * + * Taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + + /* Get the deepest lcp-interval containing the current suffix. */ + ref = pos_data[nOffset]; + + pos_data[nOffset] = 0; + + /* Ascend until we reach a visited interval, the root, or a child of the + * root. Link unvisited intervals to the current suffix as we go. */ + while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) { + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + ref = super_ref; + } + + if (super_ref == 0) { + /* In this case, the current interval may be any of: + * (1) the root; + * (2) an unvisited child of the root */ + + if (ref != 0) /* Not the root? */ + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + return 0; + } + + /* Ascend indirectly via pos_data[] links. */ + match_pos = super_ref & EXCL_VISITED_MASK; + matchptr = pMatches; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET) { + matchptr->length = (unsigned short)(ref >> (LCP_SHIFT + TAG_BITS)); + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + + for (;;) { + if ((super_ref = pos_data[match_pos]) > ref) { + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + matchptr->length = ((unsigned short)(ref >> (LCP_SHIFT + TAG_BITS))) | 0x8000; + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + } + + while ((super_ref = pos_data[match_pos]) > ref) + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + pos_data[match_pos] = ref; + + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + if (pCompressor->format_version >= 2) { + matchptr->length = (unsigned short)(ref >> (LCP_SHIFT + TAG_BITS)); + } + else { + matchptr->length = (unsigned short)(ref >> LCP_SHIFT); + } + matchptr->offset = (unsigned short)nMatchOffset; + matchptr++; + } + } + + if (super_ref == 0) + break; + ref = super_ref; + match_pos = intervals[ref & POS_MASK] & EXCL_VISITED_MASK; + + if (pCompressor->format_version >= 2 && nInWindowSize < 65536) { + if ((matchptr - pMatches) < nMaxMatches) { + int nMatchOffset = (int)(nOffset - match_pos); + + if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) { + matchptr->length = ((unsigned short)(ref >> (LCP_SHIFT + TAG_BITS))) | 0x8000; + matchptr->offset = (unsigned short)nMatchOffset; + + if ((matchptr->length & 0x7fff) > 2) { + matchptr++; + + nPrevOffset = nMatchOffset; + } + } + } + } + } + + return (int)(matchptr - pMatches); +} + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void lzsa_skip_matches(lzsa_compressor *pCompressor, const int nStartOffset, const int nEndOffset) { + lzsa_match match; + int i; + + /* Skipping still requires scanning for matches, as this also performs a lazy update of the intervals. However, + * we don't store the matches. */ + for (i = nStartOffset; i < nEndOffset; i++) { + lzsa_find_matches_at(pCompressor, i, &match, 0, 0); + } +} + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void lzsa_find_all_matches(lzsa_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset) { + lzsa_match *pMatch = pCompressor->match; + int i; + + for (i = nStartOffset; i < nEndOffset; i++) { + int nMatches = lzsa_find_matches_at(pCompressor, i, pMatch, nMatchesPerOffset, nEndOffset - nStartOffset); + + while (nMatches < nMatchesPerOffset) { + pMatch[nMatches].length = 0; + pMatch[nMatches].offset = 0; + nMatches++; + } + + pMatch += nMatchesPerOffset; + } +} diff --git a/loader/tools/lzsa/src/matchfinder.h b/loader/tools/lzsa/src/matchfinder.h new file mode 100644 index 0000000..cc3b5ab --- /dev/null +++ b/loader/tools/lzsa/src/matchfinder.h @@ -0,0 +1,91 @@ +/* + * matchfinder.h - LZ match finder definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _MATCHFINDER_H +#define _MATCHFINDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations */ +typedef struct _lzsa_match lzsa_match; +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int lzsa_build_suffix_array(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize); + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param nMaxMatches maximum number of matches to return (0 for none) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return number of matches + */ +int lzsa_find_matches_at(lzsa_compressor *pCompressor, const int nOffset, lzsa_match *pMatches, const int nMaxMatches, const int nInWindowSize); + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void lzsa_skip_matches(lzsa_compressor *pCompressor, const int nStartOffset, const int nEndOffset); + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void lzsa_find_all_matches(lzsa_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATCHFINDER_H */ diff --git a/loader/tools/lzsa/src/shrink_block_v1.c b/loader/tools/lzsa/src/shrink_block_v1.c new file mode 100644 index 0000000..32c5c38 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_block_v1.c @@ -0,0 +1,710 @@ +/* + * shrink_block_v1.c - LZSA1 block compressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "lib.h" +#include "shrink_block_v1.h" +#include "format.h" + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int lzsa_get_literals_varlen_size_v1(const int nLength) { + if (nLength < LITERALS_RUN_LEN_V1) { + return 0; + } + else { + if (nLength < 256) + return 8; + else { + if (nLength < 512) + return 16; + else + return 24; + } + } +} + +/** + * Write extra literals length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength literals length + */ +static inline int lzsa_write_literals_varlen_v1(unsigned char *pOutData, int nOutOffset, int nLength) { + if (nLength >= LITERALS_RUN_LEN_V1) { + if (nLength < 256) + pOutData[nOutOffset++] = nLength - LITERALS_RUN_LEN_V1; + else { + if (nLength < 512) { + pOutData[nOutOffset++] = 250; + pOutData[nOutOffset++] = nLength - 256; + } + else { + pOutData[nOutOffset++] = 249; + pOutData[nOutOffset++] = nLength & 0xff; + pOutData[nOutOffset++] = (nLength >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent an encoded match length + * + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V1) + * + * @return number of extra bits required + */ +static inline int lzsa_get_match_varlen_size_v1(const int nLength) { + if (nLength < MATCH_RUN_LEN_V1) { + return 0; + } + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 256) + return 8; + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 512) + return 16; + else + return 24; + } + } +} + +/** + * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V1) + */ +static inline int lzsa_write_match_varlen_v1(unsigned char *pOutData, int nOutOffset, int nLength) { + if (nLength >= MATCH_RUN_LEN_V1) { + if ((nLength + MIN_MATCH_SIZE_V1) < 256) + pOutData[nOutOffset++] = nLength - MATCH_RUN_LEN_V1; + else { + if ((nLength + MIN_MATCH_SIZE_V1) < 512) { + pOutData[nOutOffset++] = 239; + pOutData[nOutOffset++] = nLength + MIN_MATCH_SIZE_V1 - 256; + } + else { + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = (nLength + MIN_MATCH_SIZE_V1) & 0xff; + pOutData[nOutOffset++] = ((nLength + MIN_MATCH_SIZE_V1) >> 8) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get offset encoding cost in bits + * + * @param nMatchOffset offset to get cost of + * + * @return cost in bits + */ +static inline int lzsa_get_offset_cost_v1(const unsigned int nMatchOffset) { + return (nMatchOffset <= 256) ? 8 : 16; +} + +/** + * Attempt to pick optimal matches using a forward arrivals parser, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +static void lzsa_optimize_forward_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int nReduce) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << ARRIVALS_PER_POSITION_SHIFT); + const int nMinMatchSize = pCompressor->min_match_size; + const int nFavorRatio = (pCompressor->flags & LZSA_FLAG_FAVOR_RATIO) ? 1 : 0; + const int nModeSwitchPenalty = nFavorRatio ? 0 : MODESWITCH_PENALTY; + const int nDisableScore = nReduce ? 0 : (2 * BLOCK_SIZE); + int i, j, n; + + if ((nEndOffset - nStartOffset) > BLOCK_SIZE) return; + + memset(arrival + (nStartOffset << ARRIVALS_PER_POSITION_SHIFT), 0, sizeof(lzsa_arrival) * ((nEndOffset - nStartOffset + 1) << ARRIVALS_PER_POSITION_SHIFT)); + + arrival[nStartOffset << ARRIVALS_PER_POSITION_SHIFT].from_slot = -1; + + for (i = nStartOffset; i != nEndOffset; i++) { + lzsa_arrival* cur_arrival = &arrival[i << ARRIVALS_PER_POSITION_SHIFT]; + int m; + + for (j = 0; j < NARRIVALS_PER_POSITION_V1 && cur_arrival[j].from_slot; j++) { + int nPrevCost = cur_arrival[j].cost; + int nCodingChoiceCost = nPrevCost + 8 /* literal */; + int nScore = cur_arrival[j].score + 1; + int nNumLiterals = cur_arrival[j].num_literals + 1; + + if (nNumLiterals == LITERALS_RUN_LEN_V1 || nNumLiterals == 256 || nNumLiterals == 512) { + nCodingChoiceCost += 8; + } + + if (nNumLiterals == 1) + nCodingChoiceCost += nModeSwitchPenalty; + + lzsa_arrival *pDestSlots = &arrival[(i + 1) << ARRIVALS_PER_POSITION_SHIFT]; + for (n = 0; n < NARRIVALS_PER_POSITION_V1 /* we only need the literals + short match cost + long match cost cases */; n++) { + lzsa_arrival *pDestArrival = &pDestSlots[n]; + + if (pDestArrival->from_slot == 0 || + nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + memmove(&arrival[((i + 1) << ARRIVALS_PER_POSITION_SHIFT) + n + 1], + &arrival[((i + 1) << ARRIVALS_PER_POSITION_SHIFT) + n], + sizeof(lzsa_arrival) * (NARRIVALS_PER_POSITION_V1 - n - 1)); + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + pDestArrival->rep_offset = cur_arrival[j].rep_offset; + break; + } + } + } + + const lzsa_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V1); + int nNumArrivalsForThisPos = j; + + for (m = 0; m < NMATCHES_PER_INDEX_V1 && match[m].length; m++) { + int nMatchLen = match[m].length; + int nMatchOffsetCost = lzsa_get_offset_cost_v1(match[m].offset); + int nStartingMatchLen, k; + + if ((i + nMatchLen) > nEndOffset) + nMatchLen = nEndOffset - i; + + if (nMatchLen >= LEAVE_ALONE_MATCH_SIZE) + nStartingMatchLen = nMatchLen; + else + nStartingMatchLen = nMinMatchSize; + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + int nMatchLenCost = lzsa_get_match_varlen_size_v1(k - MIN_MATCH_SIZE_V1); + + lzsa_arrival *pDestSlots = &arrival[(i + k) << ARRIVALS_PER_POSITION_SHIFT]; + + for (j = 0; j < nNumArrivalsForThisPos; j++) { + int nPrevCost = cur_arrival[j].cost; + int nCodingChoiceCost = nPrevCost + 8 /* token */ /* the actual cost of the literals themselves accumulates up the chain */ + nMatchOffsetCost + nMatchLenCost; + int exists = 0; + + if (!cur_arrival[j].num_literals) + nCodingChoiceCost += nModeSwitchPenalty; + + for (n = 0; + n < NARRIVALS_PER_POSITION_V1 && pDestSlots[n].from_slot && pDestSlots[n].cost <= nCodingChoiceCost; + n++) { + if (lzsa_get_offset_cost_v1(pDestSlots[n].rep_offset) == nMatchOffsetCost) { + exists = 1; + break; + } + } + + if (!exists) { + int nScore = cur_arrival[j].score + 5; + + for (n = 0; n < NARRIVALS_PER_POSITION_V1 /* we only need the literals + short match cost + long match cost cases */; n++) { + lzsa_arrival *pDestArrival = &pDestSlots[n]; + + if (pDestArrival->from_slot == 0 || + nCodingChoiceCost < pDestArrival->cost || + (nCodingChoiceCost == pDestArrival->cost && nScore < (pDestArrival->score + nDisableScore))) { + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (NARRIVALS_PER_POSITION_V1 - n - 1)); + + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = match[m].offset; + j = NARRIVALS_PER_POSITION_V1; + break; + } + } + } + } + } + } + } + + lzsa_arrival *end_arrival = &arrival[(i << ARRIVALS_PER_POSITION_SHIFT) + 0]; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0) { + if (end_arrival->from_pos >= nEndOffset) return; + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + if (end_arrival->match_len) + pBestMatch[end_arrival->from_pos].offset = end_arrival->rep_offset; + else + pBestMatch[end_arrival->from_pos].offset = 0; + + end_arrival = &arrival[(end_arrival->from_pos << ARRIVALS_PER_POSITION_SHIFT) + (end_arrival->from_slot - 1)]; + } +} + +/** + * Attempt to minimize the number of commands issued in the compressed data block, in order to speed up decompression without + * impacting the compression ratio + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int lzsa_optimize_command_count_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nDidReduce = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < nEndOffset && + pBestMatch[i + 1].length >= MIN_MATCH_SIZE_V1 && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= nEndOffset && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize = lzsa_get_match_varlen_size_v1(pBestMatch[i + 1].length - MIN_MATCH_SIZE_V1); + int nReducedLenSize = lzsa_get_match_varlen_size_v1(pBestMatch[i + 1].length + 1 - MIN_MATCH_SIZE_V1); + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + if (pMatch->length <= 9 /* Don't waste time considering large matches, they will always win over literals */ && + (i + pMatch->length) < nEndOffset /* Don't consider the last token in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V1) { + nNextLiterals++; + nNextIndex++; + } + + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match, or the end of the input. Calculate this command's current cost (excluding 'nNumLiterals' bytes) */ + if ((8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + ((pMatch->offset <= 256) ? 8 : 16) /* match offset */ + lzsa_get_match_varlen_size_v1(pMatch->length - MIN_MATCH_SIZE_V1) + + 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNextLiterals)) >= + (8 /* token */ + (pMatch->length << 3) + lzsa_get_literals_varlen_size_v1(nNumLiterals + pMatch->length + nNextLiterals))) { + /* Reduce */ + int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + continue; + } + } + + if ((i + pMatch->length) <= nEndOffset && pMatch->offset > 0 && pMatch->length >= MIN_MATCH_SIZE_V1 && + pBestMatch[i + pMatch->length].offset > 0 && + pBestMatch[i + pMatch->length].length >= MIN_MATCH_SIZE_V1 && + (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) > pMatch->offset && + (i + pMatch->length) > pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nCurPartialSize = lzsa_get_match_varlen_size_v1(pMatch->length - MIN_MATCH_SIZE_V1); + nCurPartialSize += 8 /* token */ + lzsa_get_literals_varlen_size_v1(0) + ((pBestMatch[i + pMatch->length].offset <= 256) ? 8 : 16) /* match offset */ + lzsa_get_match_varlen_size_v1(pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V1); + + int nReducedPartialSize = lzsa_get_match_varlen_size_v1(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V1); + + if (nCurPartialSize >= nReducedPartialSize) { + int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + continue; + } + } + + i += pMatch->length; + nNumLiterals = 0; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Get compressed data block size + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return size of compressed data that will be written to output buffer + */ +static int lzsa_get_compressed_size_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nCompressedSize = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V1; + int nTokenLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x80; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + (nTokenLongOffset ? 16 : 8) /* match offset */ + lzsa_get_match_varlen_size_v1(nEncodedMatchLen); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + i += nMatchLen; + } + else { + nNumLiterals++; + i++; + } + } + + { + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nCompressedSize += 8 * 4; + } + + return nCompressedSize; +} + +/** + * Emit block of compressed data + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_block_v1(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int i; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nOutOffset = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V1) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V1; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V1) ? MATCH_RUN_LEN_V1 : nEncodedMatchLen; + int nTokenLongOffset = (nMatchOffset <= 256) ? 0x00 : 0x80; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + (nTokenLongOffset ? 16 : 8) /* match offset */ + lzsa_get_match_varlen_size_v1(nEncodedMatchLen); + + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET) + return -1; + + pOutData[nOutOffset++] = nTokenLongOffset | (nTokenLiteralsLen << 4) | nTokenMatchLen; + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + if (nTokenLongOffset) { + pOutData[nOutOffset++] = (-nMatchOffset) >> 8; + } + nOutOffset = lzsa_write_match_varlen_v1(pOutData, nOutOffset, nEncodedMatchLen); + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3); + + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x0f; + else + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x00; + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + /* Emit EOD marker for raw block */ + + if ((nOutOffset + 4) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 0; + } + + return nOutOffset; +} + +/** + * Emit raw block of uncompressible data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_raw_uncompressed_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int nNumLiterals = nEndOffset - nStartOffset; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V1) ? LITERALS_RUN_LEN_V1 : nNumLiterals; + int nOutOffset = 0; + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v1(nNumLiterals) + (nNumLiterals << 3) + 4; + if ((nOutOffset + (nCommandSize >> 3)) > nMaxOutDataSize) + return -1; + + pCompressor->num_commands = 0; + pOutData[nOutOffset++] = (nTokenLiteralsLen << 4) | 0x0f; + + nOutOffset = lzsa_write_literals_varlen_v1(pOutData, nOutOffset, nNumLiterals); + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nStartOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + pCompressor->num_commands++; + + /* Emit EOD marker for raw block */ + + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 238; + pOutData[nOutOffset++] = 0; + pOutData[nOutOffset++] = 0; + + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA1 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nResult, nBaseCompressedSize; + + /* Compress optimally without breaking ties in favor of less tokens */ + + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v1(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* reduce */); + + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v1(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nBaseCompressedSize = lzsa_get_compressed_size_v1(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + lzsa_match *pBestMatch = pCompressor->best_match - nPreviousBlockSize; + + if (nBaseCompressedSize > 0 && nInDataSize < 65536) { + int nReducedCompressedSize; + + /* Compress optimally and do break ties in favor of less tokens */ + memset(pCompressor->improved_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v1(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v1(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nReducedCompressedSize = lzsa_get_compressed_size_v1(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nReducedCompressedSize > 0 && nReducedCompressedSize <= nBaseCompressedSize) { + /* Pick the parse with the reduced number of tokens as it didn't negatively affect the size */ + pBestMatch = pCompressor->improved_match - nPreviousBlockSize; + } + } + + nResult = lzsa_write_block_v1(pCompressor, pBestMatch, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + if (nResult < 0 && pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nResult = lzsa_write_raw_uncompressed_block_v1(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + } + + return nResult; +} diff --git a/loader/tools/lzsa/src/shrink_block_v1.h b/loader/tools/lzsa/src/shrink_block_v1.h new file mode 100644 index 0000000..cc89cde --- /dev/null +++ b/loader/tools/lzsa/src/shrink_block_v1.h @@ -0,0 +1,53 @@ +/* + * shrink_block_v1.h - LZSA1 block compressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_BLOCK_V1_H +#define _SHRINK_BLOCK_V1_H + +/* Forward declarations */ +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA1 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v1(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize); + +#endif /* _SHRINK_BLOCK_V1_H */ diff --git a/loader/tools/lzsa/src/shrink_block_v2.c b/loader/tools/lzsa/src/shrink_block_v2.c new file mode 100644 index 0000000..ca0ec45 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_block_v2.c @@ -0,0 +1,1411 @@ +/* + * shrink_block_v2.c - LZSA2 block compressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "lib.h" +#include "shrink_block_v2.h" +#include "format.h" + +/** + * Write 4-bit nibble to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nNibbleValue value to write (0..15) + */ +static int lzsa_write_nibble_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nNibbleValue) { + if (nOutOffset < 0) return -1; + + if ((*nCurNibbleOffset) == -1) { + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurNibbleOffset) = nOutOffset; + pOutData[nOutOffset++] = nNibbleValue << 4; + } + else { + pOutData[*nCurNibbleOffset] = (pOutData[*nCurNibbleOffset]) | (nNibbleValue & 0x0f); + (*nCurNibbleOffset) = -1; + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int lzsa_get_literals_varlen_size_v2(const int nLength) { + if (nLength < LITERALS_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + return 4; + } + else { + if (nLength < 256) + return 4+8; + else { + return 4+24; + } + } + } +} + +/** + * Write extra literals length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nLength literals length + */ +static inline int lzsa_write_literals_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nLength) { + if (nLength >= LITERALS_RUN_LEN_V2) { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - LITERALS_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, 15); + if (nOutOffset < 0) return -1; + + if (nLength < 256) + pOutData[nOutOffset++] = nLength - 18; + else { + pOutData[nOutOffset++] = 239; + pOutData[nOutOffset++] = (nLength >> 8) & 0xff; + pOutData[nOutOffset++] = nLength & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent an encoded match length + * + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + * + * @return number of extra bits required + */ +static inline int lzsa_get_match_varlen_size_v2(const int nLength) { + if (nLength < MATCH_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (MATCH_RUN_LEN_V2 + 15)) + return 4; + else { + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + return 4+8; + else { + return 4 + 24; + } + } + } +} + +/** + * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + */ +static inline int lzsa_write_match_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nLength) { + if (nLength >= MATCH_RUN_LEN_V2) { + if (nLength < (MATCH_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - MATCH_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, 15); + if (nOutOffset < 0) return -1; + + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + pOutData[nOutOffset++] = nLength + MIN_MATCH_SIZE_V2 - 24; + else { + pOutData[nOutOffset++] = 233; + pOutData[nOutOffset++] = ((nLength + MIN_MATCH_SIZE_V2) >> 8) & 0xff; + pOutData[nOutOffset++] = (nLength + MIN_MATCH_SIZE_V2) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Insert forward rep candidate + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param i input data window position whose matches are being considered + * @param nMatchOffset match offset to use as rep candidate + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nDepth current insertion depth + */ +static void lzsa_insert_forward_match_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, int nDepth) { + lzsa_arrival *arrival = pCompressor->arrival + ((i - nStartOffset) << ARRIVALS_PER_POSITION_SHIFT); + const int *rle_len = (int*)pCompressor->intervals /* reuse */; + lzsa_match* visited = ((lzsa_match*)pCompressor->pos_data) - nStartOffset /* reuse */; + int j; + + for (j = 0; j < NARRIVALS_PER_POSITION_V2_BIG && arrival[j].from_slot; j++) { + int nRepOffset = arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset && nRepOffset && arrival[j].rep_len >= MIN_MATCH_SIZE_V2) { + int nRepPos = arrival[j].rep_pos; + int nRepLen = arrival[j].rep_len; + + if (nRepPos > nMatchOffset && + (nRepPos + nRepLen) <= nEndOffset && + pCompressor->match[((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2) + NMATCHES_PER_INDEX_V2 - 1].length == 0) { + + if (visited[nRepPos].offset != nMatchOffset || visited[nRepPos].length > nRepLen) { + visited[nRepPos].offset = nMatchOffset; + visited[nRepPos].length = nRepLen; + + if (pInWindow[nRepPos] == pInWindow[nRepPos - nMatchOffset]) { + int nLen0 = rle_len[nRepPos - nMatchOffset]; + int nLen1 = rle_len[nRepPos]; + int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + + if (nMinLen >= nRepLen || !memcmp(pInWindow + nRepPos + nMinLen, pInWindow + nRepPos + nMinLen - nMatchOffset, nRepLen - nMinLen)) { + visited[nRepPos].length = 0; + + lzsa_match* fwd_match = pCompressor->match + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + int r; + + for (r = 0; r < NMATCHES_PER_INDEX_V2 && fwd_match[r].length >= MIN_MATCH_SIZE_V2; r++) { + if (fwd_match[r].offset == nMatchOffset) { + r = NMATCHES_PER_INDEX_V2; + break; + } + } + + if (r < NMATCHES_PER_INDEX_V2) { + int nMaxRepLen = nEndOffset - nRepPos; + if (nMaxRepLen > LCP_MAX) + nMaxRepLen = LCP_MAX; + int nCurRepLen = (nMinLen > nRepLen) ? nMinLen : nRepLen; + if (nCurRepLen > nMaxRepLen) + nCurRepLen = nMaxRepLen; + const unsigned char* pInWindowMax = pInWindow + nRepPos + nMaxRepLen; + const unsigned char* pInWindowAtRepPos = pInWindow + nRepPos + nCurRepLen; + while ((pInWindowAtRepPos + 8) < pInWindowMax && !memcmp(pInWindowAtRepPos, pInWindowAtRepPos - nMatchOffset, 8)) + pInWindowAtRepPos += 8; + while ((pInWindowAtRepPos + 4) < pInWindowMax && !memcmp(pInWindowAtRepPos, pInWindowAtRepPos - nMatchOffset, 4)) + pInWindowAtRepPos += 4; + while (pInWindowAtRepPos < pInWindowMax && pInWindowAtRepPos[0] == pInWindowAtRepPos[-nMatchOffset]) + pInWindowAtRepPos++; + + nCurRepLen = (int)(pInWindowAtRepPos - (pInWindow + nRepPos)); + fwd_match[r].offset = nMatchOffset; + fwd_match[r].length = nCurRepLen; + + if (nDepth < 9) + lzsa_insert_forward_match_v2(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nDepth + 1); + } + } + } + } + } + } + } +} + +/** + * Attempt to pick optimal matches using a forward arrivals parser, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch pointer to buffer for outputting optimal matches + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nReduce non-zero to reduce the number of tokens when the path costs are equal, zero not to + * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates + * @param nArrivalsPerPosition number of arrivals to record per input buffer position + */ +static void lzsa_optimize_forward_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int nReduce, const int nInsertForwardReps, const int nArrivalsPerPosition) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << ARRIVALS_PER_POSITION_SHIFT); + const int *rle_len = (int*)pCompressor->intervals /* reuse */; + lzsa_match *visited = ((lzsa_match*)pCompressor->pos_data) - nStartOffset /* reuse */; + char *nRepSlotHandledMask = pCompressor->rep_slot_handled_mask; + char *nRepLenHandledMask = pCompressor->rep_len_handled_mask; + const int nModeSwitchPenalty = (pCompressor->flags & LZSA_FLAG_FAVOR_RATIO) ? 0 : MODESWITCH_PENALTY; + const int nMinMatchSize = pCompressor->min_match_size; + const int nDisableScore = nReduce ? 0 : (2 * BLOCK_SIZE); + const int nMaxRepInsertedLen = nReduce ? LEAVE_ALONE_MATCH_SIZE : 0; + const int nLeaveAloneMatchSize = (nArrivalsPerPosition == NARRIVALS_PER_POSITION_V2_SMALL) ? LEAVE_ALONE_MATCH_SIZE_SMALL : LEAVE_ALONE_MATCH_SIZE; + int i, j, n; + + if ((nEndOffset - nStartOffset) > BLOCK_SIZE) return; + + memset(arrival + (nStartOffset << ARRIVALS_PER_POSITION_SHIFT), 0, sizeof(lzsa_arrival) * ((nEndOffset - nStartOffset + 1) << ARRIVALS_PER_POSITION_SHIFT)); + + for (i = (nStartOffset << ARRIVALS_PER_POSITION_SHIFT); i != ((nEndOffset + 1) << ARRIVALS_PER_POSITION_SHIFT); i++) { + arrival[i].cost = 0x40000000; + } + + arrival[nStartOffset << ARRIVALS_PER_POSITION_SHIFT].from_slot = -1; + + if (nInsertForwardReps) { + memset(visited + nStartOffset, 0, (nEndOffset - nStartOffset) * sizeof(lzsa_match)); + } + + for (i = nStartOffset; i != nEndOffset; i++) { + lzsa_arrival *cur_arrival = &arrival[i << ARRIVALS_PER_POSITION_SHIFT]; + int m; + + for (j = 0; j < nArrivalsPerPosition && cur_arrival[j].from_slot; j++) { + const int nPrevCost = cur_arrival[j].cost & 0x3fffffff; + int nCodingChoiceCost = nPrevCost + 8 /* literal */; + int nScore = cur_arrival[j].score + 1; + int nNumLiterals = cur_arrival[j].num_literals + 1; + + if (nNumLiterals == LITERALS_RUN_LEN_V2) { + nCodingChoiceCost += 4; + } + else if (nNumLiterals == (LITERALS_RUN_LEN_V2 + 15)) { + nCodingChoiceCost += 8; + } + else if (nNumLiterals == 256) { + nCodingChoiceCost += 16; + } + + if (nNumLiterals == 1) + nCodingChoiceCost += nModeSwitchPenalty; + + lzsa_arrival *pDestSlots = &cur_arrival[1 << ARRIVALS_PER_POSITION_SHIFT]; + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && nScore < (pDestSlots[nArrivalsPerPosition - 1].score + nDisableScore))) { + int nRepOffset = cur_arrival[j].rep_offset; + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = cur_arrival[j].rep_pos; + pDestArrival->rep_len = cur_arrival[j].rep_len; + } + } + } + } + } + } + + lzsa_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + int nNumArrivalsForThisPos = j, nMinOverallRepLen = 0, nMaxOverallRepLen = 0; + + int nRepMatchArrivalIdxAndLen[(NARRIVALS_PER_POSITION_V2_BIG * 2) + 1]; + int nNumRepMatchArrivals = 0; + + int nMaxRepLenForPos = nEndOffset - i; + if (nMaxRepLenForPos > LCP_MAX) + nMaxRepLenForPos = LCP_MAX; + const unsigned char* pInWindowStart = pInWindow + i; + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLenForPos; + + for (j = 0; j < nNumArrivalsForThisPos && (i + MIN_MATCH_SIZE_V2) <= nEndOffset; j++) { + int nRepOffset = cur_arrival[j].rep_offset; + + if (nRepOffset) { + if (i > nRepOffset) { + if (pInWindow[i] == pInWindow[i - nRepOffset]) { + const unsigned char* pInWindowAtPos; + + int nLen0 = rle_len[i - nRepOffset]; + int nLen1 = rle_len[i]; + int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + + if (nMinLen > nMaxRepLenForPos) + nMinLen = nMaxRepLenForPos; + pInWindowAtPos = pInWindowStart + nMinLen; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos - nRepOffset, pInWindowAtPos, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos - nRepOffset, pInWindowAtPos, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[-nRepOffset] == pInWindowAtPos[0]) + pInWindowAtPos++; + int nCurRepLen = (int)(pInWindowAtPos - pInWindowStart); + + if (nCurRepLen >= MIN_MATCH_SIZE_V2) { + if (nMaxOverallRepLen < nCurRepLen) + nMaxOverallRepLen = nCurRepLen; + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals++] = j; + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals++] = nCurRepLen; + } + } + } + } + } + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals] = -1; + + if (!nReduce) { + memset(nRepSlotHandledMask, 0, nArrivalsPerPosition * ((LCP_MAX + 1) / 8) * sizeof(char)); + } + memset(nRepLenHandledMask, 0, ((LCP_MAX + 1) / 8) * sizeof(char)); + + for (m = 0; m < NMATCHES_PER_INDEX_V2 && match[m].length; m++) { + int nMatchLen = match[m].length & 0x7fff; + int nMatchOffset = match[m].offset; + int nScorePenalty = 3 + ((match[m].length & 0x8000) >> 15); + int nNoRepmatchOffsetCost = (nMatchOffset <= 32) ? 4 : ((nMatchOffset <= 512) ? 8 : ((nMatchOffset <= (8192 + 512)) ? 12 : 16)); + int nStartingMatchLen, k; + + if ((i + nMatchLen) > nEndOffset) + nMatchLen = nEndOffset - i; + + if (nInsertForwardReps) + lzsa_insert_forward_match_v2(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, 0); + + int nNonRepMatchArrivalIdx = -1; + for (j = 0; j < nNumArrivalsForThisPos; j++) { + int nRepOffset = cur_arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset) { + nNonRepMatchArrivalIdx = j; + break; + } + } + + int nMatchLenCost; + if (nMatchLen >= nLeaveAloneMatchSize) { + nStartingMatchLen = nMatchLen; + nMatchLenCost = 4 + 24 + 8 /* token */; + } + else { + nStartingMatchLen = nMinMatchSize; + nMatchLenCost = 0 + 8 /* token */; + } + + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + if (k == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) { + nMatchLenCost = 4 + 8 /* token */; + } + else { + if (k == (MATCH_RUN_LEN_V2 + 15 + MIN_MATCH_SIZE_V2)) + nMatchLenCost = 4 + 8 + 8 /* token */; + else { + if (k == 256) + nMatchLenCost = 4 + 24 + 8 /* token */; + } + } + + lzsa_arrival *pDestSlots = &cur_arrival[k << ARRIVALS_PER_POSITION_SHIFT]; + + /* Insert non-repmatch candidate */ + + if (nNonRepMatchArrivalIdx >= 0) { + const int nPrevCost = cur_arrival[nNonRepMatchArrivalIdx].cost & 0x3fffffff; + int nCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost + nNoRepmatchOffsetCost; + + if (!cur_arrival[nNonRepMatchArrivalIdx].num_literals) + nCodingChoiceCost += nModeSwitchPenalty; + + int nScore = cur_arrival[nNonRepMatchArrivalIdx].score + nScorePenalty; + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 2].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 2].cost && nScore < (pDestSlots[nArrivalsPerPosition - 2].score + nDisableScore))) { + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition - 1) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nMatchOffset && + (!nInsertForwardReps || pDestSlots[nn].rep_pos >= i || + pDestSlots[nArrivalsPerPosition - 1].from_slot)) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) + break; + } + + if (z == (nArrivalsPerPosition - 1) && pDestSlots[z].from_slot && pDestSlots[z].match_len < MIN_MATCH_SIZE_V2) + z--; + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = nNonRepMatchArrivalIdx + 1; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nMatchOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + nRepLenHandledMask[k >> 3] &= ~(1 << (k & 7)); + } + } + } + } + } + } + + /* Insert repmatch candidates */ + + if (k > nMinOverallRepLen && k <= nMaxOverallRepLen && (nRepLenHandledMask[k >> 3] & (1 << (k & 7))) == 0) { + int nCurRepMatchArrival; + + nRepLenHandledMask[k >> 3] |= 1 << (k & 7); + + for (nCurRepMatchArrival = 0; (j = nRepMatchArrivalIdxAndLen[nCurRepMatchArrival]) >= 0; nCurRepMatchArrival += 2) { + int nMaskOffset = (j << 7) + (k >> 3); + if (nRepMatchArrivalIdxAndLen[nCurRepMatchArrival + 1] >= k && (nReduce || !(nRepSlotHandledMask[nMaskOffset] & (1 << (k & 7))))) { + const int nPrevCost = cur_arrival[j].cost & 0x3fffffff; + int nRepCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost; + int nScore = cur_arrival[j].score + 2; + + if (nRepCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nRepCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && nScore < (pDestSlots[nArrivalsPerPosition - 1].score + nDisableScore))) { + int nRepOffset = cur_arrival[j].rep_offset; + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nRepCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + if (!nReduce) + nRepSlotHandledMask[nMaskOffset] |= 1 << (k & 7); + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nRepCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nRepCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nRepCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + nRepLenHandledMask[k >> 3] &= ~(1 << (k & 7)); + } + } + } + } + } + else { + break; + } + } + } + + if (k < nMaxRepInsertedLen) + nMinOverallRepLen = k; + } + } + + if (nMatchLen >= LCP_MAX && ((m + 1) >= NMATCHES_PER_INDEX_V2 || match[m + 1].length < LCP_MAX)) + break; + } + } + + lzsa_arrival *end_arrival = &arrival[(i << ARRIVALS_PER_POSITION_SHIFT) + 0]; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0) { + if (end_arrival->from_pos >= nEndOffset) return; + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + if (end_arrival->match_len) + pBestMatch[end_arrival->from_pos].offset = end_arrival->rep_offset; + else + pBestMatch[end_arrival->from_pos].offset = 0; + end_arrival = &arrival[(end_arrival->from_pos << ARRIVALS_PER_POSITION_SHIFT) + (end_arrival->from_slot - 1)]; + } +} + +/** + * Attempt to minimize the number of commands issued in the compressed data block, in order to speed up decompression without + * impacting the compression ratio + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch optimal matches to evaluate and update + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int lzsa_optimize_command_count_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nPrevRepMatchOffset = 0; + int nRepMatchOffset = 0; + int nRepMatchLen = 0; + int nRepIndex = 0; + int nDidReduce = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < nEndOffset && + pBestMatch[i + 1].length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= nEndOffset && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length - MIN_MATCH_SIZE_V2); + int nReducedLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length + 1 - MIN_MATCH_SIZE_V2); + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + if ((i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length >= MIN_MATCH_SIZE_V2) { + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match */ + + if (nRepMatchOffset && pMatch->offset != nRepMatchOffset && (pBestMatch[nNextIndex].offset != pMatch->offset || pBestMatch[nNextIndex].offset == nRepMatchOffset || + ((pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16))) > + ((pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16))))) { + /* Check if we can change the current match's offset to be the same as the previous match's offset, and get an extra repmatch. This will occur when + * matching large regions of identical bytes for instance, where there are too many offsets to be considered by the parser, and when not compressing to favor the + * ratio (the forward arrivals parser already has this covered). */ + if (i > nRepMatchOffset && + (i - nRepMatchOffset + pMatch->length) <= nEndOffset && + !memcmp(pInWindow + i - nRepMatchOffset, pInWindow + i - pMatch->offset, pMatch->length)) { + pMatch->offset = nRepMatchOffset; + nDidReduce = 1; + } + } + + if (pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset && nRepMatchOffset != pBestMatch[nNextIndex].offset) { + /* Otherwise, try to gain a match forward as well */ + if (i > pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= nEndOffset) { + int nMaxLen = 0; + while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + if (nMaxLen >= pMatch->length) { + /* Replace */ + pMatch->offset = pBestMatch[nNextIndex].offset; + nDidReduce = 1; + } + else if (nMaxLen >= 2 && pMatch->offset != nRepMatchOffset) { + int nPartialSizeBefore, nPartialSizeAfter; + + nPartialSizeBefore = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + nPartialSizeBefore += (pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + nPartialSizeBefore += lzsa_get_literals_varlen_size_v2(nNextLiterals); + + nPartialSizeAfter = lzsa_get_match_varlen_size_v2(nMaxLen - MIN_MATCH_SIZE_V2); + nPartialSizeAfter += lzsa_get_literals_varlen_size_v2(nNextLiterals + (pMatch->length - nMaxLen)) + ((pMatch->length - nMaxLen) << 3); + + if (nPartialSizeAfter < nPartialSizeBefore) { + int j; + + /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but + * we have calculated that this is shorter */ + pMatch->offset = pBestMatch[nNextIndex].offset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pMatch->length < 9 /* Don't waste time considering large matches, they will always win over literals */) { + /* Calculate this command's current cost (excluding 'nNumLiterals' bytes) */ + + int nCurCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + if (pMatch->offset != nRepMatchOffset) + nCurCommandSize += (pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + + /* Calculate the next command's current cost */ + int nNextCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNextLiterals) + /* (nNextLiterals << 3) + */ lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != pMatch->offset) + nNextCommandSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize; + + /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals (excluding 'nNumLiterals' bytes) */ + int nReducedCommandSize = (pMatch->length << 3) + 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals + pMatch->length + nNextLiterals) + /* (nNextLiterals << 3) + */ lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != nRepMatchOffset) + nReducedCommandSize += (pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nReplaceRepOffset = 0; + if (nRepMatchOffset && nRepMatchOffset != nPrevRepMatchOffset && nRepMatchLen >= MIN_MATCH_SIZE_V2 && nRepMatchOffset != pBestMatch[nNextIndex].offset && nRepIndex > pBestMatch[nNextIndex].offset && + (nRepIndex - pBestMatch[nNextIndex].offset + nRepMatchLen) <= nEndOffset && + !memcmp(pInWindow + nRepIndex - nRepMatchOffset, pInWindow + nRepIndex - pBestMatch[nNextIndex].offset, nRepMatchLen)) { + /* Replacing this match command by literals would let us create a repmatch */ + nReplaceRepOffset = 1; + nReducedCommandSize -= (nRepMatchOffset <= 32) ? 4 : ((nRepMatchOffset <= 512) ? 8 : ((nRepMatchOffset <= (8192 + 512)) ? 12 : 16)); + } + + if (nOriginalCombinedCommandSize >= nReducedCommandSize) { + /* Reduce */ + int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + + if (nReplaceRepOffset) { + pBestMatch[nRepIndex].offset = pBestMatch[nNextIndex].offset; + nRepMatchOffset = pBestMatch[nNextIndex].offset; + } + continue; + } + } + } + } + + if ((i + pMatch->length) <= nEndOffset && pMatch->offset > 0 && pMatch->length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + pMatch->length].offset > 0 && + pBestMatch[i + pMatch->length].length >= MIN_MATCH_SIZE_V2 && + (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) > pMatch->offset && + (i + pMatch->length) > pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nNextIndex = i + pMatch->length; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextIndex++; + } + + int nNextOffset; + if (nNextIndex < nEndOffset) + nNextOffset = pBestMatch[nNextIndex].offset; + else + nNextOffset = 0; + + int nCurPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + + nCurPartialSize += 8 /* token */ + /* lzsa_get_literals_varlen_size_v2(0) + */ lzsa_get_match_varlen_size_v2(pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[i + pMatch->length].offset != pMatch->offset) + nCurPartialSize += (pBestMatch[i + pMatch->length].offset <= 32) ? 4 : ((pBestMatch[i + pMatch->length].offset <= 512) ? 8 : ((pBestMatch[i + pMatch->length].offset <= (8192 + 512)) ? 12 : 16)); + + if (nNextOffset != pBestMatch[i + pMatch->length].offset) + nCurPartialSize += (nNextOffset <= 32) ? 4 : ((nNextOffset <= 512) ? 8 : ((nNextOffset <= (8192 + 512)) ? 12 : 16)); + + int nReducedPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + + if (nNextOffset != pMatch->offset) + nReducedPartialSize += (nNextOffset <= 32) ? 4 : ((nNextOffset <= 512) ? 8 : ((nNextOffset <= (8192 + 512)) ? 12 : 16)); + + if (nCurPartialSize >= nReducedPartialSize) { + int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + nDidReduce = 1; + continue; + } + } + + nPrevRepMatchOffset = nRepMatchOffset; + nRepMatchOffset = pMatch->offset; + nRepMatchLen = pMatch->length; + nRepIndex = i; + + i += pMatch->length; + nNumLiterals = 0; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Get compressed data block size + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return size of compressed data that will be written to output buffer + */ +static int lzsa_get_compressed_size_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nRepMatchOffset = 0; + int nCompressedSize = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nOffsetSize; + + if (nMatchOffset == nRepMatchOffset) { + nOffsetSize = 0; + } + else { + if (nMatchOffset <= 32) { + nOffsetSize = 4; + } + else if (nMatchOffset <= 512) { + nOffsetSize = 8; + } + else if (nMatchOffset <= (8192 + 512)) { + nOffsetSize = 12; + } + else { + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + nCompressedSize += nCommandSize; + + nNumLiterals = 0; + nRepMatchOffset = nMatchOffset; + i += nMatchLen; + } + else { + nNumLiterals++; + i++; + } + } + + { + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nCompressedSize += (8 + 4); + } + + return nCompressedSize; +} + +/** + * Emit block of compressed data + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_block_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int i; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nOutOffset = 0; + int nCurNibbleOffset = -1; + int nRepMatchOffset = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + //XXX TODO check here also for overrun of match for inline depacking + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V2) ? MATCH_RUN_LEN_V2 : nEncodedMatchLen; + int nTokenOffsetMode; + int nOffsetSize; + + /* rep */ + if (nMatchOffset == nRepMatchOffset) { + nTokenOffsetMode = 0xe0; + nOffsetSize = 0; + } + else { + /* 5 bits */ + if (nMatchOffset <= 32) { + nTokenOffsetMode = 0x00 | ((((-nMatchOffset) & 0x01) << 6)); + nOffsetSize = 4; + } + /* 9 bits XXX TODO could use add 0x20 to make use of full offset range? */ + else if (nMatchOffset <= 512) { + nTokenOffsetMode = 0x80 | ((((-nMatchOffset) & 0x100) >> 3)); + nOffsetSize = 8; + } + /* 13 bits */ + else if (nMatchOffset <= (8192 + 512)) { + nTokenOffsetMode = 0x20 | ((((-(nMatchOffset - 512)) & 0x0100) >> 2)); + nOffsetSize = 12; + } + /* 16 bits */ + else { + nTokenOffsetMode = 0xc0; + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET) + return -1; + + pOutData[nOutOffset++] = nTokenOffsetMode | (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | (nTokenMatchLen << MATCH_LEN_POS_V2); + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if ((nTokenOffsetMode & 0x80) == 0x00) { + if ((nTokenOffsetMode & 0x20) == 0x00) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, ((-nMatchOffset) & 0x1e) >> 1); + if (nOutOffset < 0) return -1; + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, ((-(nMatchOffset - 512)) >> 9) & 0x0f); + if (nOutOffset < 0) return -1; + pOutData[nOutOffset++] = (-(nMatchOffset - 512)) & 0xff; + } + } + else { + if ((nTokenOffsetMode & 0xc0) == 0x80) { + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + else if ((nTokenOffsetMode & 0xe0) == 0xc0) { + pOutData[nOutOffset++] = (-nMatchOffset) >> 8; + pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + } + + if (nMatchOffset == nRepMatchOffset) + pCompressor->stats.num_rep_offsets++; + + nRepMatchOffset = nMatchOffset; + + nOutOffset = lzsa_write_match_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nEncodedMatchLen); + if (nOutOffset < 0) return -1; + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) + pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0xe0 | (MATCH_RUN_LEN_V2 << MATCH_LEN_POS_V2); + else { + //printf ("nOut first: %x\n", nOutOffset); + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + pCompressor->end_position = nOutOffset; + //pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0x00; + //endposition!!!! XXX TODO + //nEndPosition = nOutOffset; + //pCompressor->end_position = 0;//nOutOffset; + + //pCompressor->end_position = nOutOffset; + //printf ("nOut pre: %x\n", nOutOffset); + //nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + } + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + //printf ("nOut post: %x\n", nOutOffset); + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + /* Emit EOD marker for raw block */ + + if (nOutOffset >= nMaxOutDataSize) + return -1; + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + } + + return nOutOffset; +} + +/** + * Emit raw block of uncompressible data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_raw_uncompressed_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int nCurNibbleOffset = -1; + int nNumLiterals = nEndOffset - nStartOffset; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nOutOffset = 0; + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + 4 + 8; + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + pCompressor->num_commands = 0; + pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0xe0 | (MATCH_RUN_LEN_V2 << MATCH_LEN_POS_V2); + + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nStartOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + /* Emit EOD marker for raw block */ + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + + pCompressor->num_commands++; + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA2 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nResult, nBaseCompressedSize; + int nArrivalsPerPosition = (nInDataSize < 65536) ? NARRIVALS_PER_POSITION_V2_BIG : NARRIVALS_PER_POSITION_V2_SMALL; + int *rle_len = (int*)pCompressor->intervals /* reuse */; + int i; + + i = 0; + while (i < (nPreviousBlockSize + nInDataSize)) { + int nRangeStartIdx = i; + unsigned char c = pInWindow[nRangeStartIdx]; + do { + i++; + } while (i < (nPreviousBlockSize + nInDataSize) && pInWindow[i] == c); + while (nRangeStartIdx < i) { + rle_len[nRangeStartIdx] = i - nRangeStartIdx; + nRangeStartIdx++; + } + } + + /* Compress optimally without breaking ties in favor of less tokens */ + + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* reduce */, (nInDataSize < 65536) ? 1 : 0 /* insert forward reps */, nArrivalsPerPosition); + + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nBaseCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + lzsa_match *pBestMatch = pCompressor->best_match - nPreviousBlockSize; + + if (nBaseCompressedSize > 0 && nInDataSize < 65536) { + int nReducedCompressedSize; + + /* Compress optimally and do break ties in favor of less tokens */ + memset(pCompressor->improved_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */, 0 /* use forward reps */, nArrivalsPerPosition); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nReducedCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nReducedCompressedSize > 0 && nReducedCompressedSize <= nBaseCompressedSize) { + const int nEndOffset = nPreviousBlockSize + nInDataSize; + int nSupplementedCompressedSize; + + /* Pick the parse with the reduced number of tokens as it didn't negatively affect the size */ + pBestMatch = pCompressor->improved_match - nPreviousBlockSize; + + int* first_offset_for_byte = pCompressor->first_offset_for_byte; + int* next_offset_for_pos = pCompressor->next_offset_for_pos; + int nPosition; + + /* Supplement small matches */ + + memset(first_offset_for_byte, 0xff, sizeof(int) * 65536); + memset(next_offset_for_pos, 0xff, sizeof(int) * nInDataSize); + + for (nPosition = nPreviousBlockSize; nPosition < nEndOffset - 1; nPosition++) { + next_offset_for_pos[nPosition - nPreviousBlockSize] = first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)]; + first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)] = nPosition; + } + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + lzsa_match* match = pCompressor->match + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT_V2); + int m = 0, nInserted = 0; + int nMatchPos; + + while (m < 15 && match[m].length) + m++; + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < 15 && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + int nMatchOffset = nPosition - nMatchPos; + int nExistingMatchIdx; + int nAlreadyExists = 0; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + + if (!nAlreadyExists) { + int nMatchLen = 2; + while (nMatchLen < 16 && (nPosition + nMatchLen + 4) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 4)) + nMatchLen += 4; + while (nMatchLen < 16 && (nPosition + nMatchLen) < nEndOffset && pInWindow[nMatchPos + nMatchLen] == pInWindow[nPosition + nMatchLen]) + nMatchLen++; + match[m].length = nMatchLen; + match[m].offset = nMatchOffset; + m++; + nInserted++; + if (nInserted >= 15) + break; + } + } + } + + /* Compress optimally with the extra matches */ + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */, 0 /* use forward reps */, nArrivalsPerPosition); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nSupplementedCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nSupplementedCompressedSize > 0 && nSupplementedCompressedSize < nReducedCompressedSize) { + /* Pick the parse with the extra matches as it didn't negatively affect the size */ + pBestMatch = pCompressor->best_match - nPreviousBlockSize; + } + } + } + + nResult = lzsa_write_block_v2(pCompressor, pBestMatch, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + if (nResult < 0 && pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nResult = lzsa_write_raw_uncompressed_block_v2(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + } + + return nResult; +} diff --git a/loader/tools/lzsa/src/shrink_block_v2.h b/loader/tools/lzsa/src/shrink_block_v2.h new file mode 100644 index 0000000..4a83608 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_block_v2.h @@ -0,0 +1,53 @@ +/* + * shrink_block_v2.h - LZSA2 block compressor definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_BLOCK_V2_H +#define _SHRINK_BLOCK_V2_H + +/* Forward declarations */ +typedef struct _lzsa_compressor lzsa_compressor; + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA2 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize); + +#endif /* _SHRINK_BLOCK_V2_H */ diff --git a/loader/tools/lzsa/src/shrink_block_v2_zx0_encoding.c b/loader/tools/lzsa/src/shrink_block_v2_zx0_encoding.c new file mode 100644 index 0000000..c426e32 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_block_v2_zx0_encoding.c @@ -0,0 +1,1479 @@ +/* + * shrink_block_v2.c - LZSA2 block compressor implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include +#include "lib.h" +#include "shrink_block_v2.h" +#include "format.h" + +unsigned bit_size(unsigned value) { +# ifdef __GNUC__ +// enum { WORD_BITS = sizeof(unsigned) * CHAR_BIT }; + + return ((sizeof(unsigned) * CHAR_BIT - 1) ^ __builtin_clz(value)); +# else + signed bits = -1; + + do + ++bits; + while(value >>= 1); + + return bits; +# endif +} + +/** + * Write bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitOffset write index into output buffer, of current byte being filled with bits + * @param nBitValue value to write + */ +static int lzsa_write_bit_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurBitOffset, int nBitValue) { + if (nOutOffset < 0) return -1; + + if ((*nCurBitOffset) == -1) { + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurBitOffset) = nOutOffset; + pOutData[nOutOffset++] = 0x01; + } + if ((pOutData[*nCurBitOffset] & 0x80) == 0x80) { + pOutData[*nCurBitOffset] = (pOutData[*nCurBitOffset] << 1) | (nBitValue != 0); + (*nCurBitOffset) = -1; + } else { + pOutData[*nCurBitOffset] = (pOutData[*nCurBitOffset] << 1) | (nBitValue != 0); + } + + return nOutOffset; +} + +/** + * Write 4-bit nibble to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nNibbleValue value to write (0..15) + */ +static int lzsa_write_nibble_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nNibbleValue) { + if (nOutOffset < 0) return -1; + + if ((*nCurNibbleOffset) == -1) { + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurNibbleOffset) = nOutOffset; + pOutData[nOutOffset++] = nNibbleValue << 4; + } + else { + pOutData[*nCurNibbleOffset] = (pOutData[*nCurNibbleOffset]) | (nNibbleValue & 0x0f); + (*nCurNibbleOffset) = -1; + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int lzsa_get_literals_varlen_size_v2(const int nLength) { + if (nLength < LITERALS_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + return 4; + } + else { + if (nLength < 256) + return 4+8; + else { + return 4+24; + } + } + } +} + +/** + * Write extra literals length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nLength literals length + */ +static inline int lzsa_write_literals_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nLength) { + int i; + int s = bit_size(nLength) * 2 + 1 + 1; + for (i = 0; i < s; i++) nOutOffset = lzsa_write_bit_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - LITERALS_RUN_LEN_V2); + return nOutOffset; + if (nLength >= LITERALS_RUN_LEN_V2) { + if (nLength < (LITERALS_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - LITERALS_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, 15); + if (nOutOffset < 0) return -1; + + if (nLength < 256) + pOutData[nOutOffset++] = nLength - 18; + else { + pOutData[nOutOffset++] = 239; + pOutData[nOutOffset++] = (nLength >> 8) & 0xff; + pOutData[nOutOffset++] = nLength & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Get the number of extra bits required to represent an encoded match length + * + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + * + * @return number of extra bits required + */ +static inline int lzsa_get_match_varlen_size_v2(const int nLength) { + if (nLength < MATCH_RUN_LEN_V2) { + return 0; + } + else { + if (nLength < (MATCH_RUN_LEN_V2 + 15)) + return 4; + else { + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + return 4+8; + else { + return 4 + 24; + } + } + } +} + +static inline int lzsa_write_match_offset_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nLength) { + int i; + int s = bit_size(nLength >> 7) * 2 + 1; + for (i = 0; i < s; i++) nOutOffset = lzsa_write_bit_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - LITERALS_RUN_LEN_V2); + for (i = 0; i < 7; i++) { + nOutOffset = lzsa_write_bit_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - MATCH_RUN_LEN_V2); + } + return nOutOffset; +} + +/** + * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough + * room to write the bytes. + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurNibbleOffset write index into output buffer, of current byte being filled with nibbles + * @param nLength encoded match length (actual match length - MIN_MATCH_SIZE_V2) + */ +static inline int lzsa_write_match_varlen_v2(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurNibbleOffset, int nLength) { + int i; + int s = bit_size(nLength) * 2 + 1 + 1; + for (i = 0; i < s; i++) nOutOffset = lzsa_write_bit_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - LITERALS_RUN_LEN_V2); + return nOutOffset; + if (nLength >= MATCH_RUN_LEN_V2) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - MATCH_RUN_LEN_V2); + if (nLength < (MATCH_RUN_LEN_V2 + 15)) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, nLength - MATCH_RUN_LEN_V2); + } + else { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, nCurNibbleOffset, 15); + if (nOutOffset < 0) return -1; + + if ((nLength + MIN_MATCH_SIZE_V2) < 256) + pOutData[nOutOffset++] = nLength + MIN_MATCH_SIZE_V2 - 24; + else { + pOutData[nOutOffset++] = 233; + pOutData[nOutOffset++] = ((nLength + MIN_MATCH_SIZE_V2) >> 8) & 0xff; + pOutData[nOutOffset++] = (nLength + MIN_MATCH_SIZE_V2) & 0xff; + } + } + } + + return nOutOffset; +} + +/** + * Insert forward rep candidate + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param i input data window position whose matches are being considered + * @param nMatchOffset match offset to use as rep candidate + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nDepth current insertion depth + */ +static void lzsa_insert_forward_match_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, int nDepth) { + lzsa_arrival *arrival = pCompressor->arrival + ((i - nStartOffset) << ARRIVALS_PER_POSITION_SHIFT); + const int *rle_len = (int*)pCompressor->intervals /* reuse */; + lzsa_match* visited = ((lzsa_match*)pCompressor->pos_data) - nStartOffset /* reuse */; + int j; + + for (j = 0; j < NARRIVALS_PER_POSITION_V2_BIG && arrival[j].from_slot; j++) { + int nRepOffset = arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset && nRepOffset && arrival[j].rep_len >= MIN_MATCH_SIZE_V2) { + int nRepPos = arrival[j].rep_pos; + int nRepLen = arrival[j].rep_len; + + if (nRepPos > nMatchOffset && + (nRepPos + nRepLen) <= nEndOffset && + pCompressor->match[((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2) + NMATCHES_PER_INDEX_V2 - 1].length == 0) { + + if (visited[nRepPos].offset != nMatchOffset || visited[nRepPos].length > nRepLen) { + visited[nRepPos].offset = nMatchOffset; + visited[nRepPos].length = nRepLen; + + if (pInWindow[nRepPos] == pInWindow[nRepPos - nMatchOffset]) { + int nLen0 = rle_len[nRepPos - nMatchOffset]; + int nLen1 = rle_len[nRepPos]; + int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + + if (nMinLen >= nRepLen || !memcmp(pInWindow + nRepPos + nMinLen, pInWindow + nRepPos + nMinLen - nMatchOffset, nRepLen - nMinLen)) { + visited[nRepPos].length = 0; + + lzsa_match* fwd_match = pCompressor->match + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + int r; + + for (r = 0; r < NMATCHES_PER_INDEX_V2 && fwd_match[r].length >= MIN_MATCH_SIZE_V2; r++) { + if (fwd_match[r].offset == nMatchOffset) { + r = NMATCHES_PER_INDEX_V2; + break; + } + } + + if (r < NMATCHES_PER_INDEX_V2) { + int nMaxRepLen = nEndOffset - nRepPos; + if (nMaxRepLen > LCP_MAX) + nMaxRepLen = LCP_MAX; + int nCurRepLen = (nMinLen > nRepLen) ? nMinLen : nRepLen; + if (nCurRepLen > nMaxRepLen) + nCurRepLen = nMaxRepLen; + const unsigned char* pInWindowMax = pInWindow + nRepPos + nMaxRepLen; + const unsigned char* pInWindowAtRepPos = pInWindow + nRepPos + nCurRepLen; + while ((pInWindowAtRepPos + 8) < pInWindowMax && !memcmp(pInWindowAtRepPos, pInWindowAtRepPos - nMatchOffset, 8)) + pInWindowAtRepPos += 8; + while ((pInWindowAtRepPos + 4) < pInWindowMax && !memcmp(pInWindowAtRepPos, pInWindowAtRepPos - nMatchOffset, 4)) + pInWindowAtRepPos += 4; + while (pInWindowAtRepPos < pInWindowMax && pInWindowAtRepPos[0] == pInWindowAtRepPos[-nMatchOffset]) + pInWindowAtRepPos++; + + nCurRepLen = (int)(pInWindowAtRepPos - (pInWindow + nRepPos)); + fwd_match[r].offset = nMatchOffset; + fwd_match[r].length = nCurRepLen; + + if (nDepth < 9) + lzsa_insert_forward_match_v2(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nDepth + 1); + } + } + } + } + } + } + } +} + +/** + * Attempt to pick optimal matches using a forward arrivals parser, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch pointer to buffer for outputting optimal matches + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nReduce non-zero to reduce the number of tokens when the path costs are equal, zero not to + * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates + * @param nArrivalsPerPosition number of arrivals to record per input buffer position + */ +static void lzsa_optimize_forward_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int nReduce, const int nInsertForwardReps, const int nArrivalsPerPosition) { + lzsa_arrival *arrival = pCompressor->arrival - (nStartOffset << ARRIVALS_PER_POSITION_SHIFT); + const int *rle_len = (int*)pCompressor->intervals /* reuse */; + lzsa_match *visited = ((lzsa_match*)pCompressor->pos_data) - nStartOffset /* reuse */; + char *nRepSlotHandledMask = pCompressor->rep_slot_handled_mask; + char *nRepLenHandledMask = pCompressor->rep_len_handled_mask; + const int nModeSwitchPenalty = (pCompressor->flags & LZSA_FLAG_FAVOR_RATIO) ? 0 : MODESWITCH_PENALTY; + const int nMinMatchSize = pCompressor->min_match_size; + const int nDisableScore = nReduce ? 0 : (2 * BLOCK_SIZE); + const int nMaxRepInsertedLen = nReduce ? LEAVE_ALONE_MATCH_SIZE : 0; + const int nLeaveAloneMatchSize = (nArrivalsPerPosition == NARRIVALS_PER_POSITION_V2_SMALL) ? LEAVE_ALONE_MATCH_SIZE_SMALL : LEAVE_ALONE_MATCH_SIZE; + int i, j, n; + + if ((nEndOffset - nStartOffset) > BLOCK_SIZE) return; + + memset(arrival + (nStartOffset << ARRIVALS_PER_POSITION_SHIFT), 0, sizeof(lzsa_arrival) * ((nEndOffset - nStartOffset + 1) << ARRIVALS_PER_POSITION_SHIFT)); + + for (i = (nStartOffset << ARRIVALS_PER_POSITION_SHIFT); i != ((nEndOffset + 1) << ARRIVALS_PER_POSITION_SHIFT); i++) { + arrival[i].cost = 0x40000000; + } + + arrival[nStartOffset << ARRIVALS_PER_POSITION_SHIFT].from_slot = -1; + + if (nInsertForwardReps) { + memset(visited + nStartOffset, 0, (nEndOffset - nStartOffset) * sizeof(lzsa_match)); + } + + for (i = nStartOffset; i != nEndOffset; i++) { + lzsa_arrival *cur_arrival = &arrival[i << ARRIVALS_PER_POSITION_SHIFT]; + int m; + + for (j = 0; j < nArrivalsPerPosition && cur_arrival[j].from_slot; j++) { + const int nPrevCost = cur_arrival[j].cost & 0x3fffffff; + int nCodingChoiceCost = nPrevCost + 8 /* literal */; + int nScore = cur_arrival[j].score + 1; + int nNumLiterals = cur_arrival[j].num_literals + 1; + + if (nNumLiterals == LITERALS_RUN_LEN_V2) { + nCodingChoiceCost += 4; + } + else if (nNumLiterals == (LITERALS_RUN_LEN_V2 + 15)) { + nCodingChoiceCost += 8; + } + else if (nNumLiterals == 256) { + nCodingChoiceCost += 16; + } + + if (nNumLiterals == 1) + nCodingChoiceCost += nModeSwitchPenalty; + + lzsa_arrival *pDestSlots = &cur_arrival[1 << ARRIVALS_PER_POSITION_SHIFT]; + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && nScore < (pDestSlots[nArrivalsPerPosition - 1].score + nDisableScore))) { + int nRepOffset = cur_arrival[j].rep_offset; + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = cur_arrival[j].rep_pos; + pDestArrival->rep_len = cur_arrival[j].rep_len; + } + } + } + } + } + } + + lzsa_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT_V2); + int nNumArrivalsForThisPos = j, nMinOverallRepLen = 0, nMaxOverallRepLen = 0; + + int nRepMatchArrivalIdxAndLen[(NARRIVALS_PER_POSITION_V2_BIG * 2) + 1]; + int nNumRepMatchArrivals = 0; + + int nMaxRepLenForPos = nEndOffset - i; + if (nMaxRepLenForPos > LCP_MAX) + nMaxRepLenForPos = LCP_MAX; + const unsigned char* pInWindowStart = pInWindow + i; + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLenForPos; + + for (j = 0; j < nNumArrivalsForThisPos && (i + MIN_MATCH_SIZE_V2) <= nEndOffset; j++) { + int nRepOffset = cur_arrival[j].rep_offset; + + if (nRepOffset) { + if (i > nRepOffset) { + if (pInWindow[i] == pInWindow[i - nRepOffset]) { + const unsigned char* pInWindowAtPos; + + int nLen0 = rle_len[i - nRepOffset]; + int nLen1 = rle_len[i]; + int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + + if (nMinLen > nMaxRepLenForPos) + nMinLen = nMaxRepLenForPos; + pInWindowAtPos = pInWindowStart + nMinLen; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos - nRepOffset, pInWindowAtPos, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos - nRepOffset, pInWindowAtPos, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[-nRepOffset] == pInWindowAtPos[0]) + pInWindowAtPos++; + int nCurRepLen = (int)(pInWindowAtPos - pInWindowStart); + + if (nCurRepLen >= MIN_MATCH_SIZE_V2) { + if (nMaxOverallRepLen < nCurRepLen) + nMaxOverallRepLen = nCurRepLen; + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals++] = j; + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals++] = nCurRepLen; + } + } + } + } + } + nRepMatchArrivalIdxAndLen[nNumRepMatchArrivals] = -1; + + if (!nReduce) { + memset(nRepSlotHandledMask, 0, nArrivalsPerPosition * ((LCP_MAX + 1) / 8) * sizeof(char)); + } + memset(nRepLenHandledMask, 0, ((LCP_MAX + 1) / 8) * sizeof(char)); + + for (m = 0; m < NMATCHES_PER_INDEX_V2 && match[m].length; m++) { + int nMatchLen = match[m].length & 0x7fff; + int nMatchOffset = match[m].offset; + int nScorePenalty = 3 + ((match[m].length & 0x8000) >> 15); + int nNoRepmatchOffsetCost = bit_size(nMatchOffset >> 7) * 2 + 1 + 7;//(nMatchOffset <= 32) ? 4 : ((nMatchOffset <= 512) ? 8 : ((nMatchOffset <= (8192 + 512)) ? 12 : 16)); + int nStartingMatchLen, k; + + if ((i + nMatchLen) > nEndOffset) + nMatchLen = nEndOffset - i; + + if (nInsertForwardReps) + lzsa_insert_forward_match_v2(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, 0); + + int nNonRepMatchArrivalIdx = -1; + for (j = 0; j < nNumArrivalsForThisPos; j++) { + int nRepOffset = cur_arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset) { + nNonRepMatchArrivalIdx = j; + break; + } + } + + int nMatchLenCost; + if (nMatchLen >= nLeaveAloneMatchSize) { + nStartingMatchLen = nMatchLen; + nMatchLenCost = 4 + 24 + 8 /* token */; + } + else { + nStartingMatchLen = nMinMatchSize; + nMatchLenCost = 0 + 8 /* token */; + } + + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + if (k == (MATCH_RUN_LEN_V2 + MIN_MATCH_SIZE_V2)) { + nMatchLenCost = 4 + 8 /* token */; + } + else { + if (k == (MATCH_RUN_LEN_V2 + 15 + MIN_MATCH_SIZE_V2)) + nMatchLenCost = 4 + 8 + 8 /* token */; + else { + if (k == 256) + nMatchLenCost = 4 + 24 + 8 /* token */; + } + } + + lzsa_arrival *pDestSlots = &cur_arrival[k << ARRIVALS_PER_POSITION_SHIFT]; + + /* Insert non-repmatch candidate */ + + if (nNonRepMatchArrivalIdx >= 0) { + const int nPrevCost = cur_arrival[nNonRepMatchArrivalIdx].cost & 0x3fffffff; + int nCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost + nNoRepmatchOffsetCost; + + if (!cur_arrival[nNonRepMatchArrivalIdx].num_literals) + nCodingChoiceCost += nModeSwitchPenalty; + + int nScore = cur_arrival[nNonRepMatchArrivalIdx].score + nScorePenalty; + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 2].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 2].cost && nScore < (pDestSlots[nArrivalsPerPosition - 2].score + nDisableScore))) { + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition - 1) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nMatchOffset && + (!nInsertForwardReps || pDestSlots[nn].rep_pos >= i || + pDestSlots[nArrivalsPerPosition - 1].from_slot)) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) + break; + } + + if (z == (nArrivalsPerPosition - 1) && pDestSlots[z].from_slot && pDestSlots[z].match_len < MIN_MATCH_SIZE_V2) + z--; + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = nNonRepMatchArrivalIdx + 1; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nMatchOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + nRepLenHandledMask[k >> 3] &= ~(1 << (k & 7)); + } + } + } + } + } + } + + /* Insert repmatch candidates */ + + if (k > nMinOverallRepLen && k <= nMaxOverallRepLen && (nRepLenHandledMask[k >> 3] & (1 << (k & 7))) == 0) { + int nCurRepMatchArrival; + + nRepLenHandledMask[k >> 3] |= 1 << (k & 7); + + for (nCurRepMatchArrival = 0; (j = nRepMatchArrivalIdxAndLen[nCurRepMatchArrival]) >= 0; nCurRepMatchArrival += 2) { + int nMaskOffset = (j << 7) + (k >> 3); + if (nRepMatchArrivalIdxAndLen[nCurRepMatchArrival + 1] >= k && (nReduce || !(nRepSlotHandledMask[nMaskOffset] & (1 << (k & 7))))) { + const int nPrevCost = cur_arrival[j].cost & 0x3fffffff; + int nRepCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost; + int nScore = cur_arrival[j].score + 2; + + if (nRepCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nRepCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && nScore < (pDestSlots[nArrivalsPerPosition - 1].score + nDisableScore))) { + int nRepOffset = cur_arrival[j].rep_offset; + int exists = 0; + + for (n = 0; + n < nArrivalsPerPosition && pDestSlots[n].cost < nRepCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + if (!nReduce) + nRepSlotHandledMask[nMaskOffset] |= 1 << (k & 7); + break; + } + } + + if (!exists) { + for (; + n < nArrivalsPerPosition && pDestSlots[n].cost == nRepCodingChoiceCost && nScore >= (pDestSlots[n].score + nDisableScore); + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + if (n < nArrivalsPerPosition) { + int nn; + + for (nn = n; + nn < nArrivalsPerPosition && pDestSlots[nn].cost == nRepCodingChoiceCost; + nn++) { + if (pDestSlots[nn].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(lzsa_arrival) * (z - n)); + + lzsa_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nRepCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = i; + pDestArrival->rep_len = k; + nRepLenHandledMask[k >> 3] &= ~(1 << (k & 7)); + } + } + } + } + } + else { + break; + } + } + } + + if (k < nMaxRepInsertedLen) + nMinOverallRepLen = k; + } + } + + if (nMatchLen >= LCP_MAX && ((m + 1) >= NMATCHES_PER_INDEX_V2 || match[m + 1].length < LCP_MAX)) + break; + } + } + + lzsa_arrival *end_arrival = &arrival[(i << ARRIVALS_PER_POSITION_SHIFT) + 0]; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0) { + if (end_arrival->from_pos >= nEndOffset) return; + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + if (end_arrival->match_len) + pBestMatch[end_arrival->from_pos].offset = end_arrival->rep_offset; + else + pBestMatch[end_arrival->from_pos].offset = 0; + end_arrival = &arrival[(end_arrival->from_pos << ARRIVALS_PER_POSITION_SHIFT) + (end_arrival->from_slot - 1)]; + } +} + +/** + * Attempt to minimize the number of commands issued in the compressed data block, in order to speed up decompression without + * impacting the compression ratio + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param pBestMatch optimal matches to evaluate and update + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int lzsa_optimize_command_count_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nPrevRepMatchOffset = 0; + int nRepMatchOffset = 0; + int nRepMatchLen = 0; + int nRepIndex = 0; + int nDidReduce = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < nEndOffset && + pBestMatch[i + 1].length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= nEndOffset && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length - MIN_MATCH_SIZE_V2); + int nReducedLenSize = lzsa_get_match_varlen_size_v2(pBestMatch[i + 1].length + 1 - MIN_MATCH_SIZE_V2); + + if ((nReducedLenSize - nCurLenSize) <= 8) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + if ((i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length >= MIN_MATCH_SIZE_V2) { + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match */ + + if (nRepMatchOffset && pMatch->offset != nRepMatchOffset && (pBestMatch[nNextIndex].offset != pMatch->offset || pBestMatch[nNextIndex].offset == nRepMatchOffset || + ((bit_size(pMatch->offset) >> 7) * 2 + 1 + 7) > ((bit_size(pBestMatch[nNextIndex].offset) >> 7) * 2 + 1 + 7))) { + // ((pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16))) > + // ((pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16))))) { + /* Check if we can change the current match's offset to be the same as the previous match's offset, and get an extra repmatch. This will occur when + * matching large regions of identical bytes for instance, where there are too many offsets to be considered by the parser, and when not compressing to favor the + * ratio (the forward arrivals parser already has this covered). */ + if (i > nRepMatchOffset && + (i - nRepMatchOffset + pMatch->length) <= nEndOffset && + !memcmp(pInWindow + i - nRepMatchOffset, pInWindow + i - pMatch->offset, pMatch->length)) { + pMatch->offset = nRepMatchOffset; + nDidReduce = 1; + } + } + + if (pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset && nRepMatchOffset != pBestMatch[nNextIndex].offset) { + /* Otherwise, try to gain a match forward as well */ + if (i > pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= nEndOffset) { + int nMaxLen = 0; + while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + if (nMaxLen >= pMatch->length) { + /* Replace */ + pMatch->offset = pBestMatch[nNextIndex].offset; + nDidReduce = 1; + } + else if (nMaxLen >= 2 && pMatch->offset != nRepMatchOffset) { + int nPartialSizeBefore, nPartialSizeAfter; + + nPartialSizeBefore = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + nPartialSizeBefore += bit_size(pMatch->offset >> 7) * 2 + 1 + 7; //(pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + nPartialSizeBefore += lzsa_get_literals_varlen_size_v2(nNextLiterals); + + nPartialSizeAfter = lzsa_get_match_varlen_size_v2(nMaxLen - MIN_MATCH_SIZE_V2); + nPartialSizeAfter += lzsa_get_literals_varlen_size_v2(nNextLiterals + (pMatch->length - nMaxLen)) + ((pMatch->length - nMaxLen) << 3); + + if (nPartialSizeAfter < nPartialSizeBefore) { + int j; + + /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but + * we have calculated that this is shorter */ + pMatch->offset = pBestMatch[nNextIndex].offset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pMatch->length < 9 /* Don't waste time considering large matches, they will always win over literals */) { + /* Calculate this command's current cost (excluding 'nNumLiterals' bytes) */ + + int nCurCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + if (pMatch->offset != nRepMatchOffset) + nCurCommandSize += bit_size(pMatch->offset >> 7) * 2 + 1 + 7;//(pMatch->offset <= 32) ? 4 : ((pMatch->offset <= 512) ? 8 : ((pMatch->offset <= (8192 + 512)) ? 12 : 16)); + + /* Calculate the next command's current cost */ + int nNextCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNextLiterals) + /* (nNextLiterals << 3) + */ lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != pMatch->offset) + nNextCommandSize += bit_size(pBestMatch[nNextIndex].offset >> 7) * 2 + 1 + 7;//(pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize; + + /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals (excluding 'nNumLiterals' bytes) */ + int nReducedCommandSize = (pMatch->length << 3) + 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals + pMatch->length + nNextLiterals) + /* (nNextLiterals << 3) + */ lzsa_get_match_varlen_size_v2(pBestMatch[nNextIndex].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[nNextIndex].offset != nRepMatchOffset) + nReducedCommandSize += bit_size(pBestMatch[nNextIndex].offset >> 7) * 2 + 1 + 7;//(pBestMatch[nNextIndex].offset <= 32) ? 4 : ((pBestMatch[nNextIndex].offset <= 512) ? 8 : ((pBestMatch[nNextIndex].offset <= (8192 + 512)) ? 12 : 16)); + + int nReplaceRepOffset = 0; + if (nRepMatchOffset && nRepMatchOffset != nPrevRepMatchOffset && nRepMatchLen >= MIN_MATCH_SIZE_V2 && nRepMatchOffset != pBestMatch[nNextIndex].offset && nRepIndex > pBestMatch[nNextIndex].offset && + (nRepIndex - pBestMatch[nNextIndex].offset + nRepMatchLen) <= nEndOffset && + !memcmp(pInWindow + nRepIndex - nRepMatchOffset, pInWindow + nRepIndex - pBestMatch[nNextIndex].offset, nRepMatchLen)) { + /* Replacing this match command by literals would let us create a repmatch */ + nReplaceRepOffset = 1; + nReducedCommandSize -= bit_size(nRepMatchOffset >> 7) * 2 + 1 + 7;//(nRepMatchOffset <= 32) ? 4 : ((nRepMatchOffset <= 512) ? 8 : ((nRepMatchOffset <= (8192 + 512)) ? 12 : 16)); + } + + if (nOriginalCombinedCommandSize >= nReducedCommandSize) { + /* Reduce */ + int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + + if (nReplaceRepOffset) { + pBestMatch[nRepIndex].offset = pBestMatch[nNextIndex].offset; + nRepMatchOffset = pBestMatch[nNextIndex].offset; + } + continue; + } + } + } + } + + if ((i + pMatch->length) <= nEndOffset && pMatch->offset > 0 && pMatch->length >= MIN_MATCH_SIZE_V2 && + pBestMatch[i + pMatch->length].offset > 0 && + pBestMatch[i + pMatch->length].length >= MIN_MATCH_SIZE_V2 && + (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) > pMatch->offset && + (i + pMatch->length) > pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nNextIndex = i + pMatch->length; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < MIN_MATCH_SIZE_V2) { + nNextIndex++; + } + + int nNextOffset; + if (nNextIndex < nEndOffset) + nNextOffset = pBestMatch[nNextIndex].offset; + else + nNextOffset = 0; + + int nCurPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length - MIN_MATCH_SIZE_V2); + + nCurPartialSize += 8 /* token */ + /* lzsa_get_literals_varlen_size_v2(0) + */ lzsa_get_match_varlen_size_v2(pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + if (pBestMatch[i + pMatch->length].offset != pMatch->offset) + nCurPartialSize += bit_size(pBestMatch[i + pMatch->length].offset >> 7) * 2 + 1 + 7;//(pBestMatch[i + pMatch->length].offset <= 32) ? 4 : ((pBestMatch[i + pMatch->length].offset <= 512) ? 8 : ((pBestMatch[i + pMatch->length].offset <= (8192 + 512)) ? 12 : 16)); + + if (nNextOffset != pBestMatch[i + pMatch->length].offset) + nCurPartialSize += bit_size(nNextOffset >> 7) * 2 + 1 + 7;//(nNextOffset <= 32) ? 4 : ((nNextOffset <= 512) ? 8 : ((nNextOffset <= (8192 + 512)) ? 12 : 16)); + + int nReducedPartialSize = lzsa_get_match_varlen_size_v2(pMatch->length + pBestMatch[i + pMatch->length].length - MIN_MATCH_SIZE_V2); + + if (nNextOffset != pMatch->offset) + nReducedPartialSize += bit_size(nNextOffset >> 7) * 2 + 1 + 7;//(nNextOffset <= 32) ? 4 : ((nNextOffset <= 512) ? 8 : ((nNextOffset <= (8192 + 512)) ? 12 : 16)); + + if (nCurPartialSize >= nReducedPartialSize) { + int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].offset = 0; + pBestMatch[i + nMatchLen].length = -1; + nDidReduce = 1; + continue; + } + } + + nPrevRepMatchOffset = nRepMatchOffset; + nRepMatchOffset = pMatch->offset; + nRepMatchLen = pMatch->length; + nRepIndex = i; + + i += pMatch->length; + nNumLiterals = 0; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Get compressed data block size + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * + * @return size of compressed data that will be written to output buffer + */ +static int lzsa_get_compressed_size_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const int nStartOffset, const int nEndOffset) { + int i; + int nNumLiterals = 0; + int nRepMatchOffset = 0; + int nCompressedSize = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nOffsetSize; + + if (nMatchOffset == nRepMatchOffset) { + nOffsetSize = 0; + } + else { + if (nMatchOffset <= 32) { + nOffsetSize = 4; + } + else if (nMatchOffset <= 512) { + nOffsetSize = 8; + } + else if (nMatchOffset <= (8192 + 512)) { + nOffsetSize = 12; + } + else { + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + nCompressedSize += nCommandSize; + + nNumLiterals = 0; + nRepMatchOffset = nMatchOffset; + i += nMatchLen; + } + else { + nNumLiterals++; + i++; + } + } + + { + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + nCompressedSize += nCommandSize; + nNumLiterals = 0; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nCompressedSize += (8 + 4); + } + + return nCompressedSize; +} + +/** + * Emit block of compressed data + * + * @param pCompressor compression context + * @param pBestMatch optimal matches to emit + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_block_v2(lzsa_compressor *pCompressor, lzsa_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int i; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nOutOffset = 0; + int nCurNibbleOffset = -1; + int nRepMatchOffset = 0; + + for (i = nStartOffset; i < nEndOffset; ) { + const lzsa_match *pMatch = pBestMatch + i; + + //XXX TODO check here also for overrun of match for inline depacking + if (pMatch->length >= MIN_MATCH_SIZE_V2) { + int nMatchOffset = pMatch->offset; + int nMatchLen = pMatch->length; + int nEncodedMatchLen = nMatchLen - MIN_MATCH_SIZE_V2; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nTokenMatchLen = (nEncodedMatchLen >= MATCH_RUN_LEN_V2) ? MATCH_RUN_LEN_V2 : nEncodedMatchLen; + int nTokenOffsetMode; + int nOffsetSize; + + /* rep */ + if (nMatchOffset == nRepMatchOffset) { + nTokenOffsetMode = 0xe0; + nOffsetSize = 0; + } + else { + /* 5 bits */ + if (nMatchOffset <= 32) { + nTokenOffsetMode = 0x00 | ((((-nMatchOffset) & 0x01) << 6)); + nOffsetSize = 4; + } + /* 9 bits XXX TODO could use add 0x20 to make use of full offset range? */ + else if (nMatchOffset <= 512) { + nTokenOffsetMode = 0x80 | ((((-nMatchOffset) & 0x100) >> 3)); + nOffsetSize = 8; + } + /* 13 bits */ + else if (nMatchOffset <= (8192 + 512)) { + nTokenOffsetMode = 0x20 | ((((-(nMatchOffset - 512)) & 0x0100) >> 2)); + nOffsetSize = 12; + } + /* 16 bits */ + else { + nTokenOffsetMode = 0xc0; + nOffsetSize = 16; + } + } + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + nOffsetSize /* match offset */ + lzsa_get_match_varlen_size_v2(nEncodedMatchLen); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + if (nMatchOffset < MIN_OFFSET || nMatchOffset > MAX_OFFSET) + return -1; + + //pOutData[nOutOffset++] = nTokenOffsetMode | (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | (nTokenMatchLen << MATCH_LEN_POS_V2); + + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + if ((nTokenOffsetMode & 0x80) == 0x00) { + if ((nTokenOffsetMode & 0x20) == 0x00) { + //nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, ((-nMatchOffset) & 0x1e) >> 1); + //if (nOutOffset < 0) return -1; + } + else { + //nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, ((-(nMatchOffset - 512)) >> 9) & 0x0f); + //if (nOutOffset < 0) return -1; + //pOutData[nOutOffset++] = (-(nMatchOffset - 512)) & 0xff; + } + } + else { + if ((nTokenOffsetMode & 0xc0) == 0x80) { + //pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + else if ((nTokenOffsetMode & 0xe0) == 0xc0) { + //pOutData[nOutOffset++] = (-nMatchOffset) >> 8; + //pOutData[nOutOffset++] = (-nMatchOffset) & 0xff; + } + } + + if (nMatchOffset == nRepMatchOffset) + pCompressor->stats.num_rep_offsets++; + + if (nMatchOffset != nRepMatchOffset) + nOutOffset = lzsa_write_match_offset_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nMatchOffset); + + nRepMatchOffset = nMatchOffset; + + nOutOffset = lzsa_write_match_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nEncodedMatchLen); + if (nOutOffset < 0) return -1; + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + { + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3); + + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + //pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0xe0 | (MATCH_RUN_LEN_V2 << MATCH_LEN_POS_V2); + } else { + printf ("nOut first: %x\n", nOutOffset); + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + pCompressor->end_position = nOutOffset; + //pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0x00; + //endposition!!!! XXX TODO + //nEndPosition = nOutOffset; + //pCompressor->end_position = 0;//nOutOffset; + + //pCompressor->end_position = nOutOffset; + printf ("nOut pre: %x\n", nOutOffset); + //nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + } + if (nOutOffset < 0) return -1; + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + printf ("nOut post: %x\n", nOutOffset); + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->safe_dist < nCurSafeDist) + pCompressor->safe_dist = nCurSafeDist; + } + + pCompressor->num_commands++; + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + /* Emit EOD marker for raw block */ + + if (nOutOffset >= nMaxOutDataSize) + return -1; + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + } + + return nOutOffset; +} + +/** + * Emit raw block of uncompressible data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int lzsa_write_raw_uncompressed_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, const int nMaxOutDataSize) { + int nCurNibbleOffset = -1; + int nNumLiterals = nEndOffset - nStartOffset; + int nTokenLiteralsLen = (nNumLiterals >= LITERALS_RUN_LEN_V2) ? LITERALS_RUN_LEN_V2 : nNumLiterals; + int nOutOffset = 0; + + int nCommandSize = 8 /* token */ + lzsa_get_literals_varlen_size_v2(nNumLiterals) + (nNumLiterals << 3) + 4 + 8; + if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize) + return -1; + + pCompressor->num_commands = 0; + pOutData[nOutOffset++] = (nTokenLiteralsLen << LITERALS_LEN_POS_V2) | 0xe0 | (MATCH_RUN_LEN_V2 << MATCH_LEN_POS_V2); + + nOutOffset = lzsa_write_literals_varlen_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, nNumLiterals); + if (nOutOffset < 0) return -1; + + if (nNumLiterals != 0) { + memcpy(pOutData + nOutOffset, pInWindow + nStartOffset, nNumLiterals); + nOutOffset += nNumLiterals; + nNumLiterals = 0; + } + + /* Emit EOD marker for raw block */ + + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 15); /* Extended match length nibble */ + if (nOutOffset < 0) return -1; + + if ((nOutOffset + 1) > nMaxOutDataSize) + return -1; + + pOutData[nOutOffset++] = 232; /* EOD match length byte */ + + pCompressor->num_commands++; + + if (nCurNibbleOffset != -1) { + nOutOffset = lzsa_write_nibble_v2(pOutData, nOutOffset, nMaxOutDataSize, &nCurNibbleOffset, 0); + if (nOutOffset < 0 || nCurNibbleOffset != -1) + return -1; + } + + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed LZSA2 data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_optimize_and_write_block_v2(lzsa_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nResult, nBaseCompressedSize; + int nArrivalsPerPosition = (nInDataSize < 65536) ? NARRIVALS_PER_POSITION_V2_BIG : NARRIVALS_PER_POSITION_V2_SMALL; + int *rle_len = (int*)pCompressor->intervals /* reuse */; + int i; + + i = 0; + while (i < (nPreviousBlockSize + nInDataSize)) { + int nRangeStartIdx = i; + unsigned char c = pInWindow[nRangeStartIdx]; + do { + i++; + } while (i < (nPreviousBlockSize + nInDataSize) && pInWindow[i] == c); + while (nRangeStartIdx < i) { + rle_len[nRangeStartIdx] = i - nRangeStartIdx; + nRangeStartIdx++; + } + } + + /* Compress optimally without breaking ties in favor of less tokens */ + + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* reduce */, (nInDataSize < 65536) ? 1 : 0 /* insert forward reps */, nArrivalsPerPosition); + + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nBaseCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + lzsa_match *pBestMatch = pCompressor->best_match - nPreviousBlockSize; + + if (nBaseCompressedSize > 0 && nInDataSize < 65536) { + int nReducedCompressedSize; + + /* Compress optimally and do break ties in favor of less tokens */ + memset(pCompressor->improved_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */, 0 /* use forward reps */, nArrivalsPerPosition); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nReducedCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->improved_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nReducedCompressedSize > 0 && nReducedCompressedSize <= nBaseCompressedSize) { + const int nEndOffset = nPreviousBlockSize + nInDataSize; + int nSupplementedCompressedSize; + + /* Pick the parse with the reduced number of tokens as it didn't negatively affect the size */ + pBestMatch = pCompressor->improved_match - nPreviousBlockSize; + + int* first_offset_for_byte = pCompressor->first_offset_for_byte; + int* next_offset_for_pos = pCompressor->next_offset_for_pos; + int nPosition; + + /* Supplement small matches */ + + memset(first_offset_for_byte, 0xff, sizeof(int) * 65536); + memset(next_offset_for_pos, 0xff, sizeof(int) * nInDataSize); + + for (nPosition = nPreviousBlockSize; nPosition < nEndOffset - 1; nPosition++) { + next_offset_for_pos[nPosition - nPreviousBlockSize] = first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)]; + first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)] = nPosition; + } + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + lzsa_match* match = pCompressor->match + ((nPosition - nPreviousBlockSize) << MATCHES_PER_INDEX_SHIFT_V2); + int m = 0, nInserted = 0; + int nMatchPos; + + while (m < 15 && match[m].length) + m++; + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < 15 && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + int nMatchOffset = nPosition - nMatchPos; + int nExistingMatchIdx; + int nAlreadyExists = 0; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + + if (!nAlreadyExists) { + int nMatchLen = 2; + while (nMatchLen < 16 && (nPosition + nMatchLen + 4) < nEndOffset && !memcmp(pInWindow + nMatchPos + nMatchLen, pInWindow + nPosition + nMatchLen, 4)) + nMatchLen += 4; + while (nMatchLen < 16 && (nPosition + nMatchLen) < nEndOffset && pInWindow[nMatchPos + nMatchLen] == pInWindow[nPosition + nMatchLen]) + nMatchLen++; + match[m].length = nMatchLen; + match[m].offset = nMatchOffset; + m++; + nInserted++; + if (nInserted >= 15) + break; + } + } + } + + /* Compress optimally with the extra matches */ + memset(pCompressor->best_match, 0, BLOCK_SIZE * sizeof(lzsa_match)); + lzsa_optimize_forward_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* reduce */, 0 /* use forward reps */, nArrivalsPerPosition); + + nPasses = 0; + do { + nDidReduce = lzsa_optimize_command_count_v2(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + nPasses++; + } while (nDidReduce && nPasses < 20); + + nSupplementedCompressedSize = lzsa_get_compressed_size_v2(pCompressor, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + if (nSupplementedCompressedSize > 0 && nSupplementedCompressedSize < nReducedCompressedSize) { + /* Pick the parse with the extra matches as it didn't negatively affect the size */ + pBestMatch = pCompressor->best_match - nPreviousBlockSize; + } + } + } + + nResult = lzsa_write_block_v2(pCompressor, pBestMatch, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + if (nResult < 0 && pCompressor->flags & LZSA_FLAG_RAW_BLOCK) { + nResult = lzsa_write_raw_uncompressed_block_v2(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nMaxOutDataSize); + } + + return nResult; +} diff --git a/loader/tools/lzsa/src/shrink_context.c b/loader/tools/lzsa/src/shrink_context.c new file mode 100644 index 0000000..7a3c541 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_context.c @@ -0,0 +1,264 @@ +/* + * shrink_context.c - compression context implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_context.h" +#include "shrink_block_v1.h" +#include "shrink_block_v2.h" +#include "format.h" +#include "matchfinder.h" +#include "lib.h" + +/** + * Initialize compression context + * + * @param pCompressor compression context to initialize + * @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress) + * @param nMinMatchSize minimum match size (cannot be less than MIN_MATCH_SIZE) + * @param nFlags compression flags + * + * @return 0 for success, non-zero for failure + */ +int lzsa_compressor_init(lzsa_compressor *pCompressor, const int nMaxWindowSize, const int nMinMatchSize, const int nFormatVersion, const int nFlags) { + int nResult; + int nMinMatchSizeForFormat = (nFormatVersion == 1) ? MIN_MATCH_SIZE_V1 : MIN_MATCH_SIZE_V2; + int nMaxMinMatchForFormat = (nFormatVersion == 1) ? 5 : 3; + + nResult = divsufsort_init(&pCompressor->divsufsort_context); + pCompressor->intervals = NULL; + pCompressor->pos_data = NULL; + pCompressor->open_intervals = NULL; + pCompressor->match = NULL; + pCompressor->best_match = NULL; + pCompressor->improved_match = NULL; + pCompressor->arrival = NULL; + pCompressor->rep_slot_handled_mask = NULL; + pCompressor->rep_len_handled_mask = NULL; + pCompressor->first_offset_for_byte = NULL; + pCompressor->next_offset_for_pos = NULL; + pCompressor->min_match_size = nMinMatchSize; + if (pCompressor->min_match_size < nMinMatchSizeForFormat) + pCompressor->min_match_size = nMinMatchSizeForFormat; + else if (pCompressor->min_match_size > nMaxMinMatchForFormat) + pCompressor->min_match_size = nMaxMinMatchForFormat; + pCompressor->format_version = nFormatVersion; + pCompressor->flags = nFlags; + pCompressor->safe_dist = 0; + pCompressor->num_commands = 0; + + memset(&pCompressor->stats, 0, sizeof(pCompressor->stats)); + pCompressor->stats.min_literals = -1; + pCompressor->stats.min_match_len = -1; + pCompressor->stats.min_offset = -1; + pCompressor->stats.min_rle1_len = -1; + pCompressor->stats.min_rle2_len = -1; + pCompressor->end_position = -1; + + if (!nResult) { + pCompressor->intervals = (unsigned int *)malloc(nMaxWindowSize * sizeof(unsigned int)); + + if (pCompressor->intervals) { + pCompressor->pos_data = (unsigned int *)malloc(nMaxWindowSize * sizeof(unsigned int)); + + if (pCompressor->pos_data) { + pCompressor->open_intervals = (unsigned int *)malloc((LCP_AND_TAG_MAX + 1) * sizeof(unsigned int)); + + if (pCompressor->open_intervals) { + pCompressor->arrival = (lzsa_arrival *)malloc(((BLOCK_SIZE + 1) << ARRIVALS_PER_POSITION_SHIFT) * sizeof(lzsa_arrival)); + + if (pCompressor->arrival) { + pCompressor->best_match = (lzsa_match *)malloc(BLOCK_SIZE * sizeof(lzsa_match)); + + if (pCompressor->best_match) { + pCompressor->improved_match = (lzsa_match *)malloc(BLOCK_SIZE * sizeof(lzsa_match)); + + if (pCompressor->improved_match) { + if (pCompressor->format_version == 2) + pCompressor->match = (lzsa_match *)malloc(BLOCK_SIZE * NMATCHES_PER_INDEX_V2 * sizeof(lzsa_match)); + else + pCompressor->match = (lzsa_match *)malloc(BLOCK_SIZE * NMATCHES_PER_INDEX_V1 * sizeof(lzsa_match)); + if (pCompressor->match) { + if (pCompressor->format_version == 2) { + pCompressor->rep_slot_handled_mask = (char*)malloc(NARRIVALS_PER_POSITION_V2_BIG * ((LCP_MAX + 1) / 8) * sizeof(char)); + if (pCompressor->rep_slot_handled_mask) { + pCompressor->rep_len_handled_mask = (char*)malloc(((LCP_MAX + 1) / 8) * sizeof(char)); + if (pCompressor->rep_len_handled_mask) { + pCompressor->first_offset_for_byte = (int*)malloc(65536 * sizeof(int)); + if (pCompressor->first_offset_for_byte) { + pCompressor->next_offset_for_pos = (int*)malloc(BLOCK_SIZE * sizeof(int)); + if (pCompressor->next_offset_for_pos) { + return 0; + } + } + } + } + } + else { + return 0; + } + } + } + } + } + } + } + } + } + + lzsa_compressor_destroy(pCompressor); + return 100; +} + +/** + * Clean up compression context and free up any associated resources + * + * @param pCompressor compression context to clean up + */ +void lzsa_compressor_destroy(lzsa_compressor *pCompressor) { + divsufsort_destroy(&pCompressor->divsufsort_context); + + if (pCompressor->next_offset_for_pos) { + free(pCompressor->next_offset_for_pos); + pCompressor->next_offset_for_pos = NULL; + } + + if (pCompressor->first_offset_for_byte) { + free(pCompressor->first_offset_for_byte); + pCompressor->first_offset_for_byte = NULL; + } + + if (pCompressor->rep_len_handled_mask) { + free(pCompressor->rep_len_handled_mask); + pCompressor->rep_len_handled_mask = NULL; + } + + if (pCompressor->rep_slot_handled_mask) { + free(pCompressor->rep_slot_handled_mask); + pCompressor->rep_slot_handled_mask = NULL; + } + + if (pCompressor->match) { + free(pCompressor->match); + pCompressor->match = NULL; + } + + if (pCompressor->improved_match) { + free(pCompressor->improved_match); + pCompressor->improved_match = NULL; + } + + if (pCompressor->arrival) { + free(pCompressor->arrival); + pCompressor->arrival = NULL; + } + + if (pCompressor->best_match) { + free(pCompressor->best_match); + pCompressor->best_match = NULL; + } + + if (pCompressor->open_intervals) { + free(pCompressor->open_intervals); + pCompressor->open_intervals = NULL; + } + + if (pCompressor->pos_data) { + free(pCompressor->pos_data); + pCompressor->pos_data = NULL; + } + + if (pCompressor->intervals) { + free(pCompressor->intervals); + pCompressor->intervals = NULL; + } +} + +/** + * Compress one block of data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +int lzsa_compressor_shrink_block(lzsa_compressor *pCompressor, unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize) { + int nCompressedSize; + + if (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInWindow + nPreviousBlockSize, nInDataSize); + } + + if (lzsa_build_suffix_array(pCompressor, pInWindow, nPreviousBlockSize + nInDataSize)) + nCompressedSize = -1; + else { + if (nPreviousBlockSize) { + lzsa_skip_matches(pCompressor, 0, nPreviousBlockSize); + } + lzsa_find_all_matches(pCompressor, (pCompressor->format_version == 2) ? NMATCHES_PER_INDEX_V2 : NMATCHES_PER_INDEX_V1, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + + if (pCompressor->format_version == 1) { + nCompressedSize = lzsa_optimize_and_write_block_v1(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize); + if (nCompressedSize != -1 && (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData, nCompressedSize); + } + } + else if (pCompressor->format_version == 2) { + nCompressedSize = lzsa_optimize_and_write_block_v2(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize); + if (nCompressedSize != -1 && (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD)) { + lzsa_reverse_buffer(pOutData, nCompressedSize); + } + } + else { + nCompressedSize = -1; + } + } + + if (pCompressor->flags & LZSA_FLAG_RAW_BACKWARD) { + lzsa_reverse_buffer(pInWindow + nPreviousBlockSize, nInDataSize); + } + + return nCompressedSize; +} + +/** + * Get the number of compression commands issued in compressed data blocks + * + * @return number of commands + */ +int lzsa_compressor_get_command_count(lzsa_compressor *pCompressor) { + return pCompressor->num_commands; +} diff --git a/loader/tools/lzsa/src/shrink_context.h b/loader/tools/lzsa/src/shrink_context.h new file mode 100644 index 0000000..f1a33e7 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_context.h @@ -0,0 +1,184 @@ +/* + * shrink_context.h - compression context definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_CONTEXT_H +#define _SHRINK_CONTEXT_H + +#include "divsufsort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LCP_BITS 14 +#define TAG_BITS 4 +#define LCP_MAX ((1U<<(LCP_BITS - TAG_BITS)) - 1) +#define LCP_AND_TAG_MAX (1U<<(LCP_BITS - 1)) +#define LCP_SHIFT (31-LCP_BITS) +#define LCP_MASK (((1U< + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_inmem.h" +#include "shrink_context.h" +#include "frame.h" +#include "format.h" +#include "lib.h" + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t lzsa_get_max_compressed_size_inmem(size_t nInputSize) { + return lzsa_get_header_size() + ((nInputSize + (BLOCK_SIZE - 1)) >> 16) * lzsa_get_frame_size() + nInputSize + lzsa_get_frame_size() /* footer */; +} + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * + * @return actual compressed size, or -1 for error + */ +size_t lzsa_compress_inmem(unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion) { + lzsa_compressor compressor; + size_t nOriginalSize = 0; + size_t nCompressedSize = 0L; + int nResult; + int nError = 0; + + nResult = lzsa_compressor_init(&compressor, BLOCK_SIZE * 2, nMinMatchSize, nFormatVersion, nFlags); + if (nResult != 0) { + return -1; + } + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nHeaderSize = lzsa_encode_header(pOutBuffer, (int)nMaxOutBufferSize, nFormatVersion); + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + nCompressedSize += nHeaderSize; + } + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + + while (nOriginalSize < nInputSize && !nError) { + int nInDataSize; + + nInDataSize = (int)(nInputSize - nOriginalSize); + if (nInDataSize > BLOCK_SIZE) + nInDataSize = BLOCK_SIZE; + + if (nInDataSize > 0) { + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0 && nNumBlocks) { + nError = LZSA_ERROR_RAW_TOOLARGE; + break; + } + + int nOutDataSize; + int nOutDataEnd = (int)(nMaxOutBufferSize - (lzsa_get_frame_size() + nCompressedSize + lzsa_get_frame_size() /* footer */)); + int nFrameSize = lzsa_get_frame_size(); + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nFrameSize = 0; + nOutDataEnd = (int)(nMaxOutBufferSize - nCompressedSize); + } + + if (nOutDataEnd > BLOCK_SIZE) + nOutDataEnd = BLOCK_SIZE; + + nOutDataSize = lzsa_compressor_shrink_block(&compressor, pInputData + nOriginalSize - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutBuffer + nFrameSize + nCompressedSize, nOutDataEnd); + if (nOutDataSize >= 0) { + /* Write compressed block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + int nBlockheaderSize = lzsa_encode_compressed_block_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize), nOutDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + nCompressedSize += nBlockheaderSize; + } + } + + if (!nError) { + nOriginalSize += nInDataSize; + nCompressedSize += nOutDataSize; + } + } + else { + /* Write uncompressible, literal block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nError = LZSA_ERROR_RAW_UNCOMPRESSED; + break; + } + + int nBlockheaderSize = lzsa_encode_uncompressed_block_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize), nInDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if ((size_t)nInDataSize > (nMaxOutBufferSize - (nCompressedSize + nBlockheaderSize))) + nError = LZSA_ERROR_DST; + else { + memcpy(pOutBuffer + nBlockheaderSize + nCompressedSize, pInputData + nOriginalSize, nInDataSize); + + nOriginalSize += nInDataSize; + nCompressedSize += nBlockheaderSize + nInDataSize; + } + } + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + } + + if (!nError) { + int nFooterSize; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nFooterSize = 0; + } + else { + nFooterSize = lzsa_encode_footer_frame(pOutBuffer + nCompressedSize, (int)(nMaxOutBufferSize - nCompressedSize)); + if (nFooterSize < 0) + nError = LZSA_ERROR_COMPRESSION; + } + + nCompressedSize += nFooterSize; + } + + lzsa_compressor_destroy(&compressor); + + if (nError) { + return -1; + } + else { + return nCompressedSize; + } +} + diff --git a/loader/tools/lzsa/src/shrink_inmem.h b/loader/tools/lzsa/src/shrink_inmem.h new file mode 100644 index 0000000..2bd8f27 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_inmem.h @@ -0,0 +1,71 @@ +/* + * shrink_inmem.h - in-memory compression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_INMEM_H +#define _SHRINK_INMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t lzsa_get_max_compressed_size_inmem(size_t nInputSize); + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * + * @return actual compressed size, or -1 for error + */ +size_t lzsa_compress_inmem(unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion); + +#ifdef __cplusplus +} +#endif + +#endif /* _SHRINK_INMEM_H */ diff --git a/loader/tools/lzsa/src/shrink_streaming.c b/loader/tools/lzsa/src/shrink_streaming.c new file mode 100644 index 0000000..59275d2 --- /dev/null +++ b/loader/tools/lzsa/src/shrink_streaming.c @@ -0,0 +1,346 @@ +/* + * shrink_streaming.c - streaming compression implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include "shrink_streaming.h" +#include "format.h" +#include "frame.h" +#include "lib.h" +#ifdef _WIN32 +#include +#else +#include +#endif + +/** + * Delete file + * + * @param pszInFilename name of file to delete + */ +static void lzsa_delete_file(const char *pszInFilename) { +#ifdef _WIN32 + DeleteFileA(pszInFilename); +#else + remove(pszInFilename); +#endif +} + +/*-------------- File API -------------- */ + +/** + * Compress file + * + * @param pszInFilename name of input(source) file to compress + * @param pszOutFilename name of output(compressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats) { + lzsa_stream_t inStream, outStream; + void *pDictionaryData = NULL; + int nDictionaryDataSize = 0; + int nLoadAddress = 0; + unsigned char pInData[2] = { 0 }; + lzsa_status_t nStatus; + + if (lzsa_filestream_open(&inStream, pszInFilename, "rb") < 0) { + return LZSA_ERROR_SRC; + } + + if (lzsa_filestream_open(&outStream, pszOutFilename, "wb") < 0) { + inStream.close(&inStream); + return LZSA_ERROR_DST; + } + + nStatus = lzsa_dictionary_load(pszDictionaryFilename, &pDictionaryData, &nDictionaryDataSize); + + if (nStatus) { + outStream.close(&outStream); + inStream.close(&inStream); + lzsa_delete_file(pszOutFilename); + return nStatus; + } + + int nInDataSize; + nInDataSize = inStream.read(&inStream, pInData, 2); + if (nInDataSize == 2) { + nLoadAddress = pInData[0] | (pInData[1] << 8); + } else { + return LZSA_ERROR_SRC; + } + + nStatus = lzsa_compress_stream(&inStream, &outStream, pDictionaryData, nDictionaryDataSize, nFlags, nMinMatchSize, nFormatVersion, progress, pOriginalSize, pCompressedSize, pCommandCount, pSafeDist, pStats, nLoadAddress); + + lzsa_dictionary_free(&pDictionaryData); + outStream.close(&outStream); + inStream.close(&inStream); + + if (nStatus) { + lzsa_delete_file(pszOutFilename); + } + + return nStatus; +} + +/*-------------- Streaming API -------------- */ + +/** + * Compress stream + * + * @param pInStream input(source) stream to compress + * @param pOutStream output(compressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats, int nLoadAddress) { + unsigned char *pInData, *pOutData; + lzsa_compressor compressor; + long long nOriginalSize = 0LL, nCompressedSize = 0LL; + int nResult; + unsigned char cFrameData[16]; + int nError = 0; + int nRawPadding = (nFlags & LZSA_FLAG_RAW_BLOCK) ? 8 : 0; + int nSafetyMargin = 0; + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nSafetyMargin = 3; + } else { + nSafetyMargin = 0; + } + + pInData = (unsigned char*)malloc(BLOCK_SIZE * 2); + if (!pInData) { + return LZSA_ERROR_MEMORY; + } + memset(pInData, 0, BLOCK_SIZE * 2); + + pOutData = (unsigned char*)malloc(BLOCK_SIZE); + if (!pOutData) { + free(pInData); + pInData = NULL; + + return LZSA_ERROR_MEMORY; + } + memset(pOutData, 0, BLOCK_SIZE); + + nResult = lzsa_compressor_init(&compressor, BLOCK_SIZE * 2, nMinMatchSize, nFormatVersion, nFlags); + if (nResult != 0) { + free(pOutData); + pOutData = NULL; + + free(pInData); + pInData = NULL; + + return LZSA_ERROR_MEMORY; + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + + while (!pInStream->eof(pInStream) && !nError) { + int nInDataSize; + + if (nPreviousBlockSize) { + memcpy(pInData + BLOCK_SIZE - nPreviousBlockSize, pInData + BLOCK_SIZE, nPreviousBlockSize); + } + else if (nDictionaryDataSize && pDictionaryData) { + nPreviousBlockSize = nDictionaryDataSize; + memcpy(pInData + BLOCK_SIZE - nPreviousBlockSize, pDictionaryData, nPreviousBlockSize); + } + + nInDataSize = (int)pInStream->read(pInStream, pInData + BLOCK_SIZE, BLOCK_SIZE); + if (nInDataSize > 0) { + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0 && nNumBlocks) { + nError = LZSA_ERROR_RAW_TOOLARGE; + break; + } + nDictionaryDataSize = 0; + + int nOutDataSize; + + nOutDataSize = lzsa_compressor_shrink_block(&compressor, pInData + BLOCK_SIZE - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutData, ((nInDataSize + nRawPadding) >= BLOCK_SIZE) ? BLOCK_SIZE : (nInDataSize + nRawPadding)); + if (nOutDataSize >= 0) { +// if ((nFlags & LZSA_FLAG_RAW_BLOCK) == 0) { + + int nHeaderSize = 0; + int nRealLoadAddress = nInDataSize - nOutDataSize + nLoadAddress; + printf("%x %x\n", nOutDataSize, compressor.end_position); + /* New inplace LoadAddress */ + nHeaderSize = lzsa_encode_addr_le(cFrameData, 16, nRealLoadAddress + nSafetyMargin - 4); //-4 as 4 header bytes are added + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nHeaderSize) != nHeaderSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nHeaderSize; + } + /* EndAddress */ + nHeaderSize = lzsa_encode_addr_be(cFrameData, 16, nRealLoadAddress + compressor.end_position); + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nHeaderSize) != nHeaderSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nHeaderSize; + } + /* DestAddress */ + nHeaderSize = lzsa_encode_addr_be(cFrameData, 16, nLoadAddress); + if (nHeaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nHeaderSize) != nHeaderSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nHeaderSize; + } + // } + + if (!nError) { + if (pOutStream->write(pOutStream, pOutData, (size_t)nOutDataSize) != (size_t)nOutDataSize) { + nError = LZSA_ERROR_DST; + } + else { + nOriginalSize += (long long)nInDataSize; + nCompressedSize += (long long)nOutDataSize; + } + } + } + else { + /* Write uncompressible, literal block */ + + if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { + nError = LZSA_ERROR_RAW_UNCOMPRESSED; + break; + } + + int nBlockheaderSize = lzsa_encode_uncompressed_block_frame(cFrameData, 16, nInDataSize); + if (nBlockheaderSize < 0) + nError = LZSA_ERROR_COMPRESSION; + else { + if (pOutStream->write(pOutStream, cFrameData, nBlockheaderSize) != (size_t)nBlockheaderSize) { + nError = LZSA_ERROR_DST; + } + else { + if (pOutStream->write(pOutStream, pInData + BLOCK_SIZE, (size_t)nInDataSize) != (size_t)nInDataSize) { + nError = LZSA_ERROR_DST; + } + else { + nOriginalSize += (long long)nInDataSize; + nCompressedSize += (long long)nBlockheaderSize + (long long)nInDataSize; + } + } + } + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + + if (!nError && !pInStream->eof(pInStream)) { + if (progress) + progress(nOriginalSize, nCompressedSize); + } + } + + if (!nError) { + int nFooterSize = 0; + +// if ((nFlags & LZSA_FLAG_RAW_BLOCK) != 0) { +// nFooterSize = 0; +// } +// else { +// nFooterSize = lzsa_encode_footer_frame(cFrameData, 16); +// if (nFooterSize < 0) +// nError = LZSA_ERROR_COMPRESSION; +// } + + if (pOutStream->write(pOutStream, cFrameData, nFooterSize) != nFooterSize) + nError = LZSA_ERROR_DST; + nCompressedSize += (long long)nFooterSize; + } + + if (progress) + progress(nOriginalSize, nCompressedSize); + + int nCommandCount = lzsa_compressor_get_command_count(&compressor); + int nSafeDist = compressor.safe_dist; + + if (pStats) + *pStats = compressor.stats; + + lzsa_compressor_destroy(&compressor); + + free(pOutData); + pOutData = NULL; + + free(pInData); + pInData = NULL; + + if (nError) { + return nError; + } + else { + if (pOriginalSize) + *pOriginalSize = nOriginalSize; + if (pCompressedSize) + *pCompressedSize = nCompressedSize; + if (pCommandCount) + *pCommandCount = nCommandCount; + if (pSafeDist) + *pSafeDist = nSafeDist; + return LZSA_OK; + } +} diff --git a/loader/tools/lzsa/src/shrink_streaming.h b/loader/tools/lzsa/src/shrink_streaming.h new file mode 100644 index 0000000..e838c8a --- /dev/null +++ b/loader/tools/lzsa/src/shrink_streaming.h @@ -0,0 +1,99 @@ +/* + * shrink_streaming.h - streaming compression definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _SHRINK_STREAMING_H +#define _SHRINK_STREAMING_H + +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef enum _lzsa_status_t lzsa_status_t; +typedef struct _lzsa_stats lzsa_stats; + +/*-------------- File API -------------- */ + +/** + * Compress file + * + * @param pszInFilename name of input(source) file to compress + * @param pszOutFilename name of output(compressed) file to generate + * @param pszDictionaryFilename name of dictionary file, or NULL for none + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_file(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats); + +/*-------------- Streaming API -------------- */ + +/** + * Compress stream + * + * @param pInStream input(source) stream to compress + * @param pOutStream output(compressed) stream to write to + * @param pDictionaryData dictionary contents, or NULL for none + * @param nDictionaryDataSize size of dictionary contents, or 0 + * @param nFlags compression flags (LZSA_FLAG_xxx) + * @param nMinMatchSize minimum match size + * @param nFormatVersion version of format to use (1-2) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pOriginalSize pointer to returned input(source) size, updated when this function is successful + * @param pCompressedSize pointer to returned output(compressed) size, updated when this function is successful + * @param pCommandCount pointer to returned token(compression commands) count, updated when this function is successful + * @param pSafeDist pointer to return safe distance for raw blocks, updated when this function is successful + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return LZSA_OK for success, or an error value from lzsa_status_t + */ +lzsa_status_t lzsa_compress_stream(lzsa_stream_t *pInStream, lzsa_stream_t *pOutStream, const void *pDictionaryData, int nDictionaryDataSize, + const unsigned int nFlags, const int nMinMatchSize, const int nFormatVersion, + void(*progress)(long long nOriginalSize, long long nCompressedSize), long long *pOriginalSize, long long *pCompressedSize, int *pCommandCount, int *pSafeDist, lzsa_stats *pStats, int nLoadAddress); + +#ifdef __cplusplus +} +#endif + +#endif /* _SHRINK_STREAMING_H */ diff --git a/loader/tools/lzsa/src/stream.c b/loader/tools/lzsa/src/stream.c new file mode 100644 index 0000000..8937487 --- /dev/null +++ b/loader/tools/lzsa/src/stream.c @@ -0,0 +1,111 @@ +/* + * stream.c - streaming I/O implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#include +#include +#include +#include "stream.h" + +/** + * Close file stream + * + * @param stream stream + */ +static void lzsa_filestream_close(lzsa_stream_t *stream) { + if (stream->obj) { + fclose((FILE*)stream->obj); + stream->obj = NULL; + stream->read = NULL; + stream->write = NULL; + stream->eof = NULL; + stream->close = NULL; + } +} + +/** + * Read from file stream + * + * @param stream stream + * @param ptr buffer to read into + * @param size number of bytes to read + * + * @return number of bytes read + */ +static size_t lzsa_filestream_read(lzsa_stream_t *stream, void *ptr, size_t size) { + return fread(ptr, 1, size, (FILE*)stream->obj); +} + +/** + * Write to file stream + * + * @param stream stream + * @param ptr buffer to write from + * @param size number of bytes to write + * + * @return number of bytes written + */ +static size_t lzsa_filestream_write(lzsa_stream_t *stream, void *ptr, size_t size) { + return fwrite(ptr, 1, size, (FILE*)stream->obj); +} + +/** + * Check if file stream has reached the end of the data + * + * @param stream stream + * + * @return nonzero if the end of the data has been reached, 0 if there is more data + */ +static int lzsa_filestream_eof(lzsa_stream_t *stream) { + return feof((FILE*)stream->obj); +} + +/** + * Open file and create an I/O stream from it + * + * @param stream stream to fill out + * @param pszInFilename filename + * @param pszMode open mode, as with fopen() + * + * @return 0 for success, nonzero for failure + */ +int lzsa_filestream_open(lzsa_stream_t *stream, const char *pszInFilename, const char *pszMode) { + stream->obj = (void*)fopen(pszInFilename, pszMode); + if (stream->obj) { + stream->read = lzsa_filestream_read; + stream->write = lzsa_filestream_write; + stream->eof = lzsa_filestream_eof; + stream->close = lzsa_filestream_close; + return 0; + } + else + return -1; +} diff --git a/loader/tools/lzsa/src/stream.h b/loader/tools/lzsa/src/stream.h new file mode 100644 index 0000000..a8b7922 --- /dev/null +++ b/loader/tools/lzsa/src/stream.h @@ -0,0 +1,103 @@ +/* + * stream.h - streaming I/O definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4 + * With help, ideas, optimizations and speed measurements by spke + * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard + * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/ + * + */ + +#ifndef _STREAM_H +#define _STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +typedef struct _lzsa_stream_t lzsa_stream_t; + +/* I/O stream */ +typedef struct _lzsa_stream_t { + /** Opaque stream-specific pointer */ + void *obj; + + /** + * Read from stream + * + * @param stream stream + * @param ptr buffer to read into + * @param size number of bytes to read + * + * @return number of bytes read + */ + size_t(*read)(lzsa_stream_t *stream, void *ptr, size_t size); + + /** + * Write to stream + * + * @param stream stream + * @param ptr buffer to write from + * @param size number of bytes to write + * + * @return number of bytes written + */ + size_t(*write)(lzsa_stream_t *stream, void *ptr, size_t size); + + + /** + * Check if stream has reached the end of the data + * + * @param stream stream + * + * @return nonzero if the end of the data has been reached, 0 if there is more data + */ + int(*eof)(lzsa_stream_t *stream); + + /** + * Close stream + * + * @param stream stream + */ + void(*close)(lzsa_stream_t *stream); +} lzsa_stream_t; + +/** + * Open file and create an I/O stream from it + * + * @param stream stream to fill out + * @param pszInFilename filename + * @param pszMode open mode, as with fopen() + * + * @return 0 for success, nonzero for failure + */ +int lzsa_filestream_open(lzsa_stream_t *stream, const char *pszInFilename, const char *pszMode); + +#ifdef __cplusplus +} +#endif + +#endif /* _STREAM_H */ diff --git a/loader/tools/nucrunch-1.0.1/Cargo.toml b/loader/tools/nucrunch-1.0.1/Cargo.toml new file mode 100644 index 0000000..99f47f5 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "nucrunch" +version = "1.0.1" # keep this in sync with Makefile and mkbinaries.sh +authors = ["Christopher Phillips "] + +[dependencies] +clap = "2.31" diff --git a/loader/tools/nucrunch-1.0.1/MANIFEST b/loader/tools/nucrunch-1.0.1/MANIFEST new file mode 100644 index 0000000..ae0e46c --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/MANIFEST @@ -0,0 +1,25 @@ +MANIFEST +Cargo.toml +Makefile +readme.txt +decrunch.a65 +rdecrunch.a65 +decrunch.s +rdecrunch.s +srdecrunch.s +boot.s +sboot.s +src/boot.rs +src/lib.rs +src/main.rs +test/Makefile +test/crc8.py +test/cbmcat +test/endaddr +test/gentest.py +test/mkrings.py +test/testbed.a65 +test/rtestbed.a65 +test/testbed_ca65.s +test/sea_test.s +test/startaddr diff --git a/loader/tools/nucrunch-1.0.1/Makefile b/loader/tools/nucrunch-1.0.1/Makefile new file mode 100644 index 0000000..10e7323 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/Makefile @@ -0,0 +1,60 @@ +CRUNCH = target/release/nucrunch +VERSION = 1.0.1 +# keep VERSION in sync with cargo.toml and mkbinaries.sh +RELEASE = release/nucrunch-$(VERSION) +CLFLAGS = -C c64-asm.cfg -u __EXEHDR__ --cpu 6502X +RUNNER = x64sc +.PHONY: release + +# +# executable and included binaries +# +$(CRUNCH): src/main.rs src/lib.rs src/boot.rs \ + bin/boot.prg bin/sboot.prg \ + bin/boot_sei.prg bin/sboot_sei.prg + cargo build --release + +bin/boot.prg: boot.s rdecrunch.s | bin + cl65 $(CLFLAGS) -o bin/boot.prg boot.s -Ln boot.sym + +bin/sboot.prg: sboot.s srdecrunch.s | bin + cl65 $(CLFLAGS) -o bin/sboot.prg sboot.s -Ln sboot.sym + +bin/boot_sei.prg: boot.s rdecrunch.s | bin + cl65 $(CLFLAGS) --asm-define NU_SEI -o bin/boot_sei.prg boot.s -Ln boot_sei.sym + +bin/sboot_sei.prg: sboot.s srdecrunch.s | bin + cl65 $(CLFLAGS) --asm-define NU_SEI -o bin/sboot_sei.prg sboot.s -Ln sboot_sei.sym + +bin: + mkdir bin + +# +# release target +# +release: + rm -f $(RELEASE).tgz + tar -s ",^,nucrunch-$(VERSION)/," -cvf $(RELEASE).tar -T MANIFEST + gzip $(RELEASE).tar + mv $(RELEASE).tar.gz $(RELEASE).tgz + +# +# test targets +# +sea: sea.prg + $(RUNNER) sea.prg +sea.prg: $(CRUNCH) test/rings.prg test/sea_test.prg Makefile + $(CRUNCH) -xvzo sea.prg test/rings.prg test/sea_test.prg -j 0x0810 -m 0x37 +test/rings.prg: test/mkrings.py + python $< +test/sea_test.prg: test/sea_test.s + cl65 -t none -o $@ $< + +# +# cleanup +# +clean: + $(RM) -rf *.{tar,o,prg,sym} target test/__pycache__ test/*.{prg,o,lst,gen,log} +cleaner: clean + $(RM) -rf bin + diff --git a/loader/tools/nucrunch-1.0.1/boot.s b/loader/tools/nucrunch-1.0.1/boot.s new file mode 100644 index 0000000..08cba45 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/boot.s @@ -0,0 +1,71 @@ +.export boot_end +.export lowmem +decrunch_p2o=decrunch_end-decrunch_dst-256 ; offset to start of second page to copy +boot_end=decrunch_src+decrunch_end-decrunch_dst +boot_length=boot_end-$0801 + +boot_start=$0801 +.ifdef NU_SEI + sei +.else + lda#$7f + sta $dc0d ; kill CIA irq +.endif + lda#$34 + sta $01 ;disable roms and IO during decrunch + + ldx#255 + txs + +: lda $0243,x ; save 0x244 to 0x333 to stack. 0x334 to 0x3ff is tape buffer + sta $0100,x + dex + bne :- + +: lda decrunch_src,x + sta decrunch_dst,x + lda decrunch_src+decrunch_p2o,x + sta decrunch_dst+decrunch_p2o,x + dex + bne :- + jmp lowmem + +decrunch_src: + .org $0244 +decrunch_dst: + .include "rdecrunch.s" +lowmem: +o_frag_copy: + lda $8000,x + sta $07e8,x + lda $8000,x + sta boot_end-256,x + dex ; if ever we need this code space back, just recompress this chunk + bne lowmem ; as the first thing to be decrunched from the stream :D + +o_stream_start: + ldx#<($1800) + lda#>($17ff) + jsr decrunch + ldy#$f0 +: lda $0100,y + sta $0243,y ; restore 0x244 to 0x333 from stack + dey + bne :- + +o_memory_config: + lda#$37 ; operand overwitten by patch_and_prepend_boot + sta $01 ; restore memory config to default +.ifdef NU_SEI + cli +.endif +o_exec_addr: + jmp $080d ; destination overwitten by patch_and_prepend_boot +decrunch_end: + + ; patch addresses + .word o_memory_config - boot_start + decrunch_src - decrunch_dst + .word o_exec_addr - boot_start + decrunch_src - decrunch_dst + .word o_stream_start - boot_start + decrunch_src - decrunch_dst + .word o_frag_copy - boot_start + decrunch_src - decrunch_dst + diff --git a/loader/tools/nucrunch-1.0.1/decrunch.a65 b/loader/tools/nucrunch-1.0.1/decrunch.a65 new file mode 100644 index 0000000..66d92c1 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/decrunch.a65 @@ -0,0 +1,248 @@ +; +; NuCrunch 1.0 +; Christopher Jam +; May 2018 +; + +#define getByte1 \ + lda (zps),y :\ + inc zps+0 :\ + bne *+4 :\ + inc zps+1 + +#define getBit1 \ + .( :\ + asl zbs1 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs1 :\ +nomore :\ + .) + +; get head of a pair of bits from the bitpair stream +; (must getBit2t precisely once before invoking again) + +#define getBit2h \ + .( :\ + asl zbs2 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs2 :\ +nomore :\ + .) + +; same, but preserving A/ trashing X. +#define getBit2hpa \ + .( :\ + asl zbs2 :\ + bne nomore :\ + tax :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs2 :\ + txa :\ +nomore :\ + .) + +; get tail of a pair of bits from the bitpair stream +#define getBit2t \ + asl zbs2 + +; get head of a quad of bits from the quad stream +; (must getBit4t precisely three times before invoking again) + +#define getBit4h \ + .( :\ + asl zbs4 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs4 :\ +nomore :\ + .) + + +; get tail of a quad of bits from the quad stream +#define getBit4t \ + asl zbs4 + +; note, trashes X. Also, carry is clear when done +#define getExpGoulombTail \ + .( :\ +ndone :\ + getBit2hpa :\ + rol :\ + getBit2t :\ + bcs ndone :\ + .) + +#define getExpGoulombTail_odd_aligned \ + .( :\ +ndone :\ + getBit2t :\ + rol :\ + getBit2hpa :\ + bcs ndone :\ + .) + +#ifdef NUCRUNCH_ALIGN_FOR_SPEED + .dsb <-$63-*,0 ; place decode_copy on a page boundary +#endif + +decrunch_zpa=$e0 ;9 bytes required + .( +zbs1 = decrunch_zpa+$00 ; 1 byte +zbs2 = decrunch_zpa+$01 ; 1 byte +zbs4 = decrunch_zpa+$02 ; 1 byte +zpc = decrunch_zpa+$03 ; 2 bytes +zps = decrunch_zpa+$05 ; 2 bytes +zpd = decrunch_zpa+$07 ; 2 bytes + +offsetm1 = zpc ; these are aliased, as never need both + + ++decrunch + stx zps+0 + sta zps+1 + ldy #0 + sty zbs1 + sty zbs2 + sty zbs4 + + ++decrunch_next_group + ldy #0 +next_segment + jsr get_byte + sta zpd+0 + jsr get_byte + sta zpd+1 + +decode_literal + + ; get count [ExpGoulomb0+1] in x + ldx#1 + getBit1 + bcc ret1 + lda#1 + getExpGoulombTail + tax +ret1 + +literal_loop + lda (zps),y + sta (zpd),y + iny + dex + bne literal_loop + + ; carry is clear either from bcc above or _getExpGoulombTail above + tya + adc zps + sta zps + bcc *+5 + inc zps+1 + clc + tya + adc zpd + sta zpd + bcc *+4 + inc zpd+1 + ldy#0 + + ; literal is always followed by copy + +decode_copy + getBit2h + bcc short_offset + lda#1 + getExpGoulombTail_odd_aligned + adc#255 + sta offsetm1+1 + getByte1 + sta offsetm1 + jmp got_high + +short_offset + lda#0 + sta offsetm1+1 + + ;ExpGoulomb k=3 + getBit4h + lda#1 + bcc no_tail + getExpGoulombTail_odd_aligned +no_tail + adc#255 + + getBit4t + rol + getBit4t + rol + getBit4t + rol + sta offsetm1 +got_high + + ldx#1 + getBit2t + bcc length_two + lda#1 + getExpGoulombTail + tax + cpx#255 + beq end_of_segment ; copy length of 256 marks end of segment +length_two + + ; note carry is clear at this point; good as we want to subtract (offsetm1+1) + lda zpd + sbc offsetm1 + sta zpc + + lda zpd+1 + sbc offsetm1+1 + sta zpc+1 + + lda (zpc),y + sta (zpd),y +copy_loop + iny +copy_src + lda (zpc),y +copy_dst + sta (zpd),y + dex + bne copy_loop + tya + + ; carry will be set from SBC above + adc zpd + sta zpd + bcc *+4 + inc zpd+1 + + ldy#0 + getBit1 + bcs jmp_decode_copy + jmp decode_literal +jmp_decode_copy + jmp decode_copy + +get_byte + getByte1 +end_of_file + rts +end_of_segment + lda offsetm1 + cmp#0 + beq end_of_file + jmp next_segment + +.) +decrunch_end diff --git a/loader/tools/nucrunch-1.0.1/decrunch.s b/loader/tools/nucrunch-1.0.1/decrunch.s new file mode 100644 index 0000000..86e2973 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/decrunch.s @@ -0,0 +1,250 @@ +; +; NuCrunch 1.0 +; Christopher Jam +; May 2018 +; + +.export decrunch_next_group +.export decrunch +.export decrunch_end + +.macro getByte1 + lda (zps),y + inc zps+0 + bne *+4 + inc zps+1 +.endmacro + +.macro getBit1 +.local nomore + asl zbs1 + bne nomore + getByte1 + sec + rol + sta zbs1 +nomore: +.endmacro + +; get head of a pair of bits from the bitpair stream +; (must getBit2t precisely once before invoking again) + +.macro getBit2h +.local nomore + asl zbs2 + bne nomore + getByte1 + sec + rol + sta zbs2 +nomore: +.endmacro + +; same, but preserving A/ trashing X. +.macro getBit2hpa +.local nomore + asl zbs2 + bne nomore + tax + getByte1 + sec + rol + sta zbs2 + txa +nomore: +.endmacro + +; get tail of a pair of bits from the bitpair stream +.macro getBit2t + asl zbs2 +.endmacro + +; get head of a quad of bits from the quad stream +; (must getBit4t precisely three times before invoking again) + +.macro getBit4h +.local nomore + asl zbs4 + bne nomore + getByte1 + sec + rol + sta zbs4 +nomore: +.endmacro + + +; get tail of a quad of bits from the quad stream +.macro getBit4t + asl zbs4 +.endmacro + +; note, trashes X. Also, carry is clear when done +.macro getExpGoulombTail +.local ndone +ndone: + getBit2hpa + rol + getBit2t + bcs ndone +.endmacro + +.macro getExpGoulombTail_odd_aligned +.local ndone +ndone: + getBit2t + rol + getBit2hpa + bcs ndone +.endmacro + + +decrunch_zpa=$e0 ;9 bytes required + +zbs1 = decrunch_zpa+$00 ; 1 byte +zbs2 = decrunch_zpa+$01 ; 1 byte +zbs4 = decrunch_zpa+$02 ; 1 byte +zpc = decrunch_zpa+$03 ; 2 bytes +zps = decrunch_zpa+$05 ; 2 bytes +zpd = decrunch_zpa+$07 ; 2 bytes + +offsetm1 = zpc ; these are aliased, as never need both + + +decrunch: + stx zps+0 + sta zps+1 + ldy #0 + sty zbs1 + sty zbs2 + sty zbs4 + + +decrunch_next_group: + ldy #0 +next_segment: + jsr get_byte + sta zpd+0 + jsr get_byte + sta zpd+1 + +decode_literal: + + ; get count [ExpGoulomb0+1] in x + ldx#1 + getBit1 + bcc ret1 + lda#1 + getExpGoulombTail + tax +ret1: + +literal_loop: + lda (zps),y + sta (zpd),y + iny + dex + bne literal_loop + + ; carry is clear either from bcc above or _getExpGoulombTail above + tya + adc zps + sta zps + bcc *+5 + inc zps+1 + clc + tya + adc zpd + sta zpd + bcc *+4 + inc zpd+1 + ldy#0 + + ; literal is always followed by copy + +decode_copy: + getBit2h + bcc short_offset + lda#1 + getExpGoulombTail_odd_aligned + adc#255 + sta offsetm1+1 + getByte1 + sta offsetm1 + jmp got_high + +short_offset: + lda#0 + sta offsetm1+1 + + ;ExpGoulomb k=3 + getBit4h + lda#1 + bcc no_tail + getExpGoulombTail_odd_aligned +no_tail: + adc#255 + + getBit4t + rol + getBit4t + rol + getBit4t + rol + sta offsetm1 +got_high: + + ldx#1 + getBit2t + bcc length_two + lda#1 + getExpGoulombTail + tax + cpx#255 + beq end_of_segment ; copy length of 256 marks end of segment +length_two: + + ; note carry is clear at this point; good as we want to subtract (offsetm1+1) + lda zpd + sbc offsetm1 + sta zpc + + lda zpd+1 + sbc offsetm1+1 + sta zpc+1 + + lda (zpc),y + sta (zpd),y +copy_loop: + iny + lda (zpc),y + sta (zpd),y + dex + bne copy_loop + tya + + ; carry will be set from SBC above + adc zpd + sta zpd + bcc *+4 + inc zpd+1 + + ldy#0 + getBit1 + bcs jmp_decode_copy + jmp decode_literal +jmp_decode_copy: + jmp decode_copy + +get_byte: + getByte1 +end_of_file: + rts +end_of_segment: + lda offsetm1 + cmp#0 + beq end_of_file + jmp next_segment + +decrunch_end: + diff --git a/loader/tools/nucrunch-1.0.1/rdecrunch.a65 b/loader/tools/nucrunch-1.0.1/rdecrunch.a65 new file mode 100644 index 0000000..acc702c --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/rdecrunch.a65 @@ -0,0 +1,271 @@ +; +; NuCrunch 0.1 +; Christopher Jam +; February 2016 +; + +; next byte is fetched from mem[ mem[zps+1]*256+(mem[zps]-1)%256 ] +; ie, init pointer to start byte then increment low byte of pointer. +; Low byte is decremented before each fetch, then if it hits zero the high byte is predecremented for the next fetch +; note that getByte requires y to be zero +; also note that this mangled pointer is a tad inimical to the literal fetches -/ + +#define getByte0 \ + dec zps+0 :\ + bne *+7 :\ + jsr nextPage:\ + bvc *+4 :\ + lda (zps),y :\ + +#define _getByte0 \ + bit zps+0 :\ + bne *+4 :\ + dec zps+1 :\ + dec zps+0 :\ + lda (zps),y :\ + +#if 0 +#define getByte1 jsr get_byte +#else +#define getByte1 getByte0 +#endif + + +#define getBit1 \ + .( :\ + asl zbs1 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs1 :\ +nomore :\ + .) + +; get head of a pair of bits from the bitpair stream +; (must getBit2t precisely once before invoking again) + +#define getBit2h \ + .( :\ + asl zbs2 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs2 :\ +nomore :\ + .) + +; same, but preserving A/ trashing X. +#define getBit2hpa \ + .( :\ + asl zbs2 :\ + bne nomore :\ + tax :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs2 :\ + txa :\ +nomore :\ + .) + +#define getBit2t \ + asl zbs2 + +; get head of a quad of bits from the quad stream +; (must getBit4t precisely three times before invoking again) + +#define getBit4h \ + .( :\ + asl zbs4 :\ + bne nomore :\ + getByte1 :\ + sec :\ + rol :\ + sta zbs4 :\ +nomore :\ + .) + + +; get tail of a quad of bits from the quad stream +#define getBit4t \ + asl zbs4 + +; note, trashes X. Also, carry is clear when done +#define getExpGoulombTail \ + .( :\ +ndone :\ + getBit2hpa :\ + rol :\ + getBit2t :\ + bcs ndone :\ + .) + +#define getExpGoulombTail_odd_aligned \ + .( :\ +ndone :\ + getBit2t :\ + rol :\ + getBit2hpa :\ + bcs ndone :\ + .) + + +#ifdef NUCRUNCH_ALIGN_FOR_SPEED + .dsb <-$79-*,0 ; place decode_copy on a page boundary +#endif + +decrunch_zpa=$e0 ;7 bytes required +decrunch + .( +zps = decrunch_zpa+$00 +zbs1 = decrunch_zpa+$02 +zbs2 = decrunch_zpa+$03 +zbs4 = decrunch_zpa+$04 +offsetm1= decrunch_zpa+$05 + + stx zps+0 + sta zps+1 + ldy #0 + sty zbs1 + sty zbs2 + sty zbs4 + + ++decrunch_next_group + ldy #0 +next_segment + jsr get_byte + sta copy_dst+1 + jsr get_byte + sta copy_dst+2 + +decode_literal + +getEG0p1 ;get count [eg0+1] in x + ldx#1 + getBit1 + lda#1 + bcc ret1 + lda#1 + getExpGoulombTail + tax +ret1 + txa + sec + eor#255 + adc copy_dst+1 + sta copy_dst+1 + sta literal_dst+1 + bcs *+5 + dec copy_dst+2 + lda copy_dst+2 + sta literal_dst+2 + + + +literal_loop +literal_src + getByte1 +literal_dst + sta $f000,x + dex + bne literal_loop + + + ; literal is always followed by copy + +decode_copy + getBit2h + bcc short_offset + lda#1 + getExpGoulombTail_odd_aligned + adc#255 + sta offsetm1+1 + getByte1 + sta offsetm1 + jmp got_high + +short_offset + lda#0 + sta offsetm1+1 + + ;ExpGoulomb k=3 + getBit4h + lda#1 + bcc no_tail + getExpGoulombTail_odd_aligned +no_tail + adc#255 + + getBit4t + rol + getBit4t + rol + getBit4t + rol + sta offsetm1 +got_high + + ldx#2 + getBit2t + bcc length_two + lda#1 + getExpGoulombTail + tax + inx + beq end_of_segment ; copy length of 256 marks end of segment +length_two + + txa + eor#255 + sec + adc copy_dst+1 + sta copy_dst+1 + bcs *+6 + dec copy_dst+2 + sec + + ; note carry is set at this point; good as we want to add (offsetm1+1) + lda copy_dst+1 + adc offsetm1 + sta copy_src+1 + + lda copy_dst+2 + adc offsetm1+1 + sta copy_src+2 + +copy_loop +copy_src + lda $f000,x +copy_dst + sta $f000,x + dex + bne copy_loop + + + ldy#0 + getBit1 + bcs jmp_decode_copy + jmp decode_literal +jmp_decode_copy + jmp decode_copy + +get_byte + getByte0 +end_of_file + rts +end_of_segment + lda offsetm1 + cmp#0 + beq end_of_file + jmp next_segment +nextPage + lda (zps),y + dec zps+1 + clv + rts + +.) +decrunch_end diff --git a/loader/tools/nucrunch-1.0.1/rdecrunch.s b/loader/tools/nucrunch-1.0.1/rdecrunch.s new file mode 100644 index 0000000..31cc6b4 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/rdecrunch.s @@ -0,0 +1,276 @@ +; +; NuCrunch 0.1 +; Christopher Jam +; February 2016 +; + +; next byte is fetched from mem[ mem[zps+1]*256+(mem[zps]-1)%256 ] +; ie, init pointer to start byte then increment low byte of pointer. +; Low byte is decremented before each fetch, then if it hits zero the high byte is predecremented for the next fetch +; note that getByte requires y to be zero +; also note that this mangled pointer is a tad inimical to the literal fetches -/ + +.export decrunch_next_group +.export decrunch +.export decrunch_end + +.macro getByte0 + dec zps+0 + bne *+7 + jsr nextPage + bvc *+4 + lda (zps),y +.endmacro + +.macro _getByte0 + bit zps+0 + bne *+4 + dec zps+1 + dec zps+0 + lda (zps),y +.endmacro + +.if 0 +.define getByte1 jsr get_byte +.else +.define getByte1 getByte0 +.endif + + +.macro getBit1 +.local nomore + asl zbs1 + bne nomore + getByte1 + sec + rol + sta zbs1 +nomore: +.endmacro + +; get head of a pair of bits from the bitpair stream +; (must getBit2t precisely once before invoking again) + +.macro getBit2h +.local nomore + asl zbs2 + bne nomore + getByte1 + sec + rol + sta zbs2 +nomore: +.endmacro + +; same, but preserving A/ trashing X. +.macro getBit2hpa +.local nomore + asl zbs2 + bne nomore + tax + getByte1 + sec + rol + sta zbs2 + txa +nomore: +.endmacro + +.macro getBit2t + asl zbs2 +.endmacro + +; get head of a quad of bits from the quad stream +; (must getBit4t precisely three times before invoking again) + +.macro getBit4h +.local nomore + asl zbs4 + bne nomore + getByte1 + sec + rol + sta zbs4 +nomore: +.endmacro + + +; get tail of a quad of bits from the quad stream +.macro getBit4t + asl zbs4 +.endmacro + +; note, trashes X. Also, carry is clear when done +.macro getExpGoulombTail +.local ndone +ndone: + getBit2hpa + rol + getBit2t + bcs ndone +.endmacro + +.macro getExpGoulombTail_odd_aligned +.local ndone +ndone: + getBit2t + rol + getBit2hpa + bcs ndone +.endmacro + + +decrunch_zpa =$e0 ;7 bytes required + + +zps = decrunch_zpa+$00 +zbs1 = decrunch_zpa+$02 +zbs2 = decrunch_zpa+$03 +zbs4 = decrunch_zpa+$04 +offsetm1 = decrunch_zpa+$05 + + + +decrunch: + + stx zps+0 + sta zps+1 + ldy #0 + sty zbs1 + sty zbs2 + sty zbs4 + + +decrunch_next_group: + ldy #0 +next_segment: + jsr get_byte + sta copy_dst+1 + jsr get_byte + sta copy_dst+2 + +decode_literal: + +getEG0p1: ;get count [eg0+1] in x + ldx#1 + getBit1 + lda#1 + bcc ret1 + lda#1 + getExpGoulombTail + tax +ret1: + txa + sec + eor#255 + adc copy_dst+1 + sta copy_dst+1 + sta literal_dst+1 + bcs *+5 + dec copy_dst+2 + lda copy_dst+2 + sta literal_dst+2 + + + +literal_loop: +literal_src: + getByte1 +literal_dst: + sta $f000,x + dex + bne literal_loop + + + ; literal is always followed by copy + +decode_copy: + getBit2h + bcc short_offset + lda#1 + getExpGoulombTail_odd_aligned + adc#255 + sta offsetm1+1 + getByte1 + jmp got_high + +short_offset: + lda#0 + sta offsetm1+1 + + ;ExpGoulomb k=3 + getBit4h + lda#1 + bcc no_tail + getExpGoulombTail_odd_aligned +no_tail: + adc#255 + + getBit4t + rol + getBit4t + rol + getBit4t + rol +got_high: + sta offsetm1 + + ldx#2 + getBit2t + bcc length_two + lda#1 + getExpGoulombTail + tax + inx + beq end_of_segment ; copy length of 256 marks end of segment +length_two: + + txa + eor#255 + sec + adc copy_dst+1 + sta copy_dst+1 + bcs *+6 + dec copy_dst+2 + sec + + ; note carry is set at this point; good as we want to add (offsetm1+1) + lda copy_dst+1 + adc offsetm1 + sta copy_src+1 + + lda copy_dst+2 + adc offsetm1+1 + sta copy_src+2 + +copy_loop: +copy_src: + lda $f000,x +copy_dst: + sta $f000,x + dex + bne copy_loop + + + ldy#0 + getBit1 + bcs jmp_decode_copy + jmp decode_literal +jmp_decode_copy: + jmp decode_copy + +get_byte: + getByte0 +end_of_file: + rts +end_of_segment: + lda offsetm1 + cmp#0 + beq end_of_file + jmp next_segment +nextPage: + lda (zps),y + dec zps+1 + clv + rts + diff --git a/loader/tools/nucrunch-1.0.1/readme.txt b/loader/tools/nucrunch-1.0.1/readme.txt new file mode 100644 index 0000000..10b179c --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/readme.txt @@ -0,0 +1,117 @@ +/* + * NuCrunch 1.0.1 + * Christopher Jam + * August 2018 + */ + + +Requirements +============ + +Building nucrunch requires +- rust 1.26 or later, from rust-lang.org Not compatible with 1.25 or earlier. +- ca65, from https://cc65.github.io/cc65/. Tested against V2.13.9 and V2.17 + +The test suite also requires +- xa65 (from http://www.floodgap.com/retrotech/xa/) +- Python (tested against 2.7 and 3.6) +- Numpy ('sudo pip install numpy' should sort you for that) + +Note that xa65 is only needed if you want to test the xa translations +of the decrunch routines. + + +Building +======== + +make +cp target/release/nucrunch ~/bin # or whever you keep your executables + + + +Testing +======= + +make sea ; test self extracting archive creation + +cd test +make run ; test decrunch, as assembled with xa +make rrun ; test rdecrunch, as assembled with xa +make crun ; test decrunch, as assembled with cl65 + ; note cl65 port of rdecrunch is exercised by the sea test + +Each of the latter tests +- decrunches a fullscreen koala in two segments, +- performs a CRC check (green border for success, red for failure) +- waits one second +- decrunches an update, +- CRC checks the update. + +First image is concentric circles with a rect of grey-on-grey noise +Second image is just the concentric circles. + + + +Usage +===== + +nucrunch [FLAGS] [OPTIONS] ... --output <--sea|--load 0xLOAD_ADDRESS|--end 0xEND_ADDRESS|--auto> + +Note that you must select one of the sea, load, end or auto options. + + + +Self Extracting Archives +======================== + +For a simple repack to a self extracting .prg: + nucrunch -xo output.prg onefileinput.prg + + +By default, self extracting archives +- instead of using SEI/CLI, turn off the CIA interrupt for you and leaves it off. +- sets $01 to RAM only during decrunch, +- reenables ROMs and IO before passing control to the decompressed executable +- starts the decompressed executable by jumping to $080d + +Most of these behaviours are configurable, cf. nucrunch --help for details. + +If repacking someone else's code doesn't work, try adding the -b or -I options; +some inputs assume end of basic is set, or that the CIA timer is still running + + + +Using as a library for multiple groups of chunks +================================================ + +Use commas to delineate groups, eg + nucrunch f1g1.prg f2g1.prg, f1g2.prg, f1g3,prg f2g3.prg f3g3.prg -o out.prg -l 0x1000 + +Call decrunch to unpack the first group, then decrunch_next_group for each subsequent group + +Include either decrunch.a65 or ndecrunch.a65 in your executable, depending on your compression direction + +Define NUCRUNCH_ALIGN_FOR_SPEED to optimise alignment. (Currently only available with the .a65 sources; +aligning things with ca65 is a bit more fiddly.) + +to decrunch using rdecrunch: + + ldx #(decrunch_src-1) + jsr decrunch + +to decrunch using decrunch: + + ldx #decrunch_src + jsr decrunch + +See examples in the test directory for more details. + + +Change Notes +============ + +1.0.1 + - more idiomatic use of ca65, now works with more versions than the v2.13.0 originally tested against. + - general Makefile cleanup diff --git a/loader/tools/nucrunch-1.0.1/sboot.s b/loader/tools/nucrunch-1.0.1/sboot.s new file mode 100644 index 0000000..59bde5d --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/sboot.s @@ -0,0 +1,75 @@ +.export boot_end +.export lowmem +decrunch_p2o=decrunch_end-decrunch_dst-255 ; offset to start of second page to copy +boot_end=decrunch_src+decrunch_end-decrunch_dst +boot_length=boot_end-$0801 + +boot_start=$0801 +.ifdef NU_SEI + sei +.else + lda#$7f + sta $dc0d ; kill CIA irq +.endif + lda#$34 + sta $01 ;disable roms and IO during decrunch + + ldx#255 + txs + inx +: lda decrunch_src,x + sta decrunch_dst,x + lda decrunch_src+decrunch_p2o,x + sta decrunch_dst+decrunch_p2o,x + dex + bne :- + jmp lowmem + +decrunch_src: + .org $02a8 +decrunch_dst: + .include "srdecrunch.s" +lowmem: +o_frag_copy: + lda $8000,x + sta $07e8,x + lda $8000,x + sta boot_end-256,x + dex ; if ever we need this code space back, just recompress this chunk + bne lowmem ; as the first thing to be decrunched from the stream :D + +o_stream_start: + ldx#<($1800) + lda#>($17ff) + jsr decrunch + + lda#$37 + sta $01 + jsr $e453 ; restore $0300-$030b + +.if 0 + jsr $fd15 ; restore $0314-$0333. This is only safe if it's ok to trash the ram under fd30-fd4f +.else + ldy #$1f +: lda $fd30,y + sta $0314,y ; restore $0314-$0333 without trashing ram at fd30-fd4f + dey + bpl :- +.endif + +o_memory_config: + lda#$37 ; operand overwitten by patch_and_prepend_boot. + sta $01 ; restore memory config to default +.ifdef NU_SEI + cli +.endif +o_exec_addr: + jmp $080d ; destination overwitten by patch_and_prepend_boot. +decrunch_end: + + ; patch addresses + .word o_memory_config - boot_start + decrunch_src - decrunch_dst + .word o_exec_addr - boot_start + decrunch_src - decrunch_dst + .word o_stream_start - boot_start + decrunch_src - decrunch_dst + .word o_frag_copy - boot_start + decrunch_src - decrunch_dst + diff --git a/loader/tools/nucrunch-1.0.1/src/boot.rs b/loader/tools/nucrunch-1.0.1/src/boot.rs new file mode 100644 index 0000000..8687f06 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/src/boot.rs @@ -0,0 +1,85 @@ +use DataChunk; + +const BOOT_PRG: &[u8] = include_bytes!("../bin/boot.prg"); +const SBOOT_PRG: &[u8] = include_bytes!("../bin/sboot.prg"); +const BOOT_SEI_PRG: &[u8] = include_bytes!("../bin/boot_sei.prg"); +const SBOOT_SEI_PRG: &[u8] = include_bytes!("../bin/sboot_sei.prg"); +const OFFSET_BLOCK_SIZE: usize = 8; + +fn lo(addr: usize) -> u8 { + (addr & 0xff) as u8 +} + +fn hi(addr: usize) -> u8 { + ((addr >> 8) & 0xff) as u8 +} + +struct PatchOfsets { + memory_config: usize, + exec_addr: usize, + stream_start: usize, + frag_copy: usize, +} + +fn parse_boot(boot_prg: &[u8]) -> (Vec, PatchOfsets, usize) { + let boot_chunk = DataChunk::from_prg_contents(boot_prg); + let boot_end = boot_chunk.end_addr() as usize - OFFSET_BLOCK_SIZE; + let mut boot = boot_chunk.data; + let boot_len = boot.len(); + let offset_block = boot.split_off(boot_len - OFFSET_BLOCK_SIZE); + let read_offset = |i| offset_block[i] as usize | ((offset_block[i + 1] as usize) << 8); + let ofse = PatchOfsets { + memory_config: read_offset(0), + exec_addr: read_offset(2), + stream_start: read_offset(4), + frag_copy: read_offset(6), + }; + (boot, ofse, boot_end) +} + +pub fn patch_and_prepend_boot( + stream: &[u8], + exec_addr: u16, + memory_config: u8, + compact: bool, + sei: bool, +) -> Vec { + let boot_prg = match (compact, sei) { + (false, false) => &BOOT_PRG, + (false, true) => &BOOT_SEI_PRG, + (true, false) => &SBOOT_PRG, + (true, true) => &SBOOT_SEI_PRG, + }; + + let (mut boot, ofse, boot_end) = parse_boot(boot_prg); + + let stream_len = stream.len(); + + let data_start = 0x07e8; + let data_end = data_start + stream_len; + + // first frag_len bytes of the reversed stream are saved to the end, + // then copied down by the boot, overwriting the area the boot was + // loaded to. + let frag_len = stream_len.min(boot_end - data_start); + let remainder_len = stream_len - frag_len; + let frag_address = boot_end + remainder_len; + assert!(frag_len < 513); + + let a0 = frag_address; + let a1 = frag_address + frag_len - 256; + + boot[ofse.memory_config + 1] = memory_config; + boot[ofse.exec_addr + 1] = lo(exec_addr as usize); + boot[ofse.exec_addr + 2] = hi(exec_addr as usize); + boot[ofse.stream_start + 1] = lo(data_end); + boot[ofse.stream_start + 3] = hi(data_end - 1); + boot[ofse.frag_copy + 1] = lo(a0); + boot[ofse.frag_copy + 2] = hi(a0); + boot[ofse.frag_copy + 7] = lo(a1); + boot[ofse.frag_copy + 8] = hi(a1); + + boot.extend(&stream[frag_len..]); + boot.extend(&stream[..frag_len]); + boot +} diff --git a/loader/tools/nucrunch-1.0.1/src/lib.rs b/loader/tools/nucrunch-1.0.1/src/lib.rs new file mode 100644 index 0000000..be4255d --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/src/lib.rs @@ -0,0 +1,815 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io; +use std::io::prelude::*; +use std::path::Path; + +mod boot; + +#[derive(Clone, Debug)] +pub struct DataChunk { + pub data: Vec, + pub addr: u16, +} +impl DataChunk { + pub fn new(data: &[u8], addr: u16) -> Self { + DataChunk { + data: data.to_vec(), + addr, + } + } + pub fn from_prg_contents(prg: &[u8]) -> Self { + let data = prg[2..prg.len()].to_vec(); + let lb = prg[0] as u16; + let hb = prg[1] as u16; + DataChunk { + data: data, + addr: lb + 256 * hb, + } + } + pub fn end_addr(&self) -> u16 { + self.data.len() as u16 + self.addr + } +} + +#[derive(Clone, Debug)] +pub struct InputChunk { + pub chunk: DataChunk, + pub end_of_group: bool, +} + +#[derive(Debug, PartialEq)] +pub enum TargetLocation { + Start(u16), + End(u16), + Auto, + SelfExtracting, +} + +#[derive(Debug)] +pub struct Conf { + pub input_chunk_list: Vec, + pub output_fn: String, + pub reverse: bool, + pub zero_fill: bool, + pub sei: bool, + pub set_eob: bool, + pub load_location: TargetLocation, + pub compact: bool, + pub log_fn: Option, + pub verbose: bool, + pub memory_config: u8, + pub exec_addr: u16, +} + +impl Conf { + pub fn new_sea(fnam: String) -> Self { + Conf { + input_chunk_list: Vec::new(), + output_fn: fnam, + reverse: true, + zero_fill: false, + sei: false, + set_eob: false, + load_location: TargetLocation::SelfExtracting, + compact: false, + log_fn: None, + verbose: false, + memory_config: 0x37, + exec_addr: 0x080d, + } + } + pub fn memory_config(&mut self, memory_config: u8) { + self.memory_config = memory_config; + } + pub fn exec_addr(&mut self, exec_addr: u16) { + self.exec_addr = exec_addr; + } + pub fn add_chunk(&mut self, c: &DataChunk) { + self.input_chunk_list.push(InputChunk { + chunk: c.clone(), + end_of_group: false, + }); + } +} + +macro_rules! optlog { + ( $log:expr, $($x:expr),* ) => { if let Some(ref mut f)=$log {let _ = writeln!(f,$($x,)*);} } +} +macro_rules! optprint { + ( $cond:expr, $($x:expr),* ) => { if $cond {let _ = print!($($x,)*);} } +} +macro_rules! optprintln { + ( $cond:expr, $($x:expr),* ) => { if $cond {let _ = println!($($x,)*);} } +} +const MAX_RUN: usize = 255; + +pub fn read_prg(fnam: &str) -> Result { + let v = std::fs::read(fnam)?; + let chunk = DataChunk::from_prg_contents(&v); + println!( + "0x{:04x}-0x{:04x}: {:5} bytes read from {}", + chunk.addr, + chunk.end_addr(), + v.len(), + fnam + ); + Ok(chunk) +} + +fn write_prg(fnam: &str, chunk: &DataChunk) -> Result<(), io::Error> { + let path = Path::new(fnam); + let display = path.display(); + let mut file = File::create(&path)?; + let addrv = vec![(chunk.addr & 0xff) as u8, (chunk.addr >> 8) as u8]; + + file.write_all(&addrv)?; + file.write_all(&chunk.data)?; + println!( + "0x{:04x}-0x{:04x}: {:5} bytes written to {}", + chunk.addr, + chunk.end_addr(), + 2 + chunk.data.len(), + display + ); + Ok(()) +} + +type DecompressionCost = usize; + +const MAX_COST: DecompressionCost = 1000000000; + +fn sym_cost(_last_offset: usize, offset: usize, len: usize) -> DecompressionCost { + (if offset == 0 { + encoding_costs::egl_k(len - 1, 0) + 8 * len + } else { + assert!(len >= 2); + encoding_costs::egl_k(len - 2, 0) + encoding_costs::new_ofse(offset - 1) + 1 + }) as DecompressionCost +} + +#[derive(Debug)] +struct Token { + offset: usize, + length: usize, + cumulative_cost: DecompressionCost, +} + +impl Token { + fn new(predecessor: &Token, offset: usize, length: usize) -> Option { + if (offset == 0 && predecessor.offset > 0) || + (offset == 0 && predecessor.length > 128) || //EXTENSION REQUIRED FOR THIS + (offset > 0 && length >= 2) + { + let cumulative_cost = + predecessor.cumulative_cost + sym_cost(predecessor.offset, offset, length); + Some(Token { + offset, + length, + cumulative_cost, + }) + } else { + None + } + } +} + +fn crunch(src: &[u8]) -> Vec { + let preflight_len = 32; + + let early_out = true; + let preflight = true && (src.len() > preflight_len); + + let mut preflight_count = HashMap::new(); + + let mut last_seen = HashMap::new(); + let mut tokens: Vec = Vec::with_capacity(src.len() + 1); + tokens.push(Token { + offset: 1, + length: 0, + cumulative_cost: 0, + }); // dummy head needs offset>0 to make starting run legal + + if preflight { + for spf in 0..src.len() - preflight_len + 1 { + let key = &src[spf..spf + preflight_len]; + let count: usize = *(preflight_count.get(key).unwrap_or(&0)) + 1; + preflight_count.insert(key, count); + } + } + + for target_len in 1..src.len() + 1 { + let mut best_match: Token = Token { + offset: 0, + length: 0, + cumulative_cost: MAX_COST, + }; // this will be beaten + + let search_end = if target_len < MAX_RUN { + target_len + } else { + MAX_RUN + }; + let mut might_still_find_run_matches = true; + let mut give_up_on_copies = false; + + for length in 1..search_end + 1 { + let run_start = target_len - length; + + let predecessor = &tokens[run_start]; + + let key = &src[run_start..target_len]; + if preflight && length == preflight_len { + let k: usize = *preflight_count.get(key).unwrap_or(&0); + if k == 0 { + panic!("run_start = {}, src.len()={}, k=0", run_start, src.len()); + } + assert!(k >= 1); + if k == 1 { + give_up_on_copies = true; + } + } + + if !give_up_on_copies { + if might_still_find_run_matches || !early_out { + if let Some(match_start) = last_seen.get(key) { + if let Some(candidate) = + Token::new(predecessor, run_start - match_start, length) + { + if candidate.cumulative_cost <= best_match.cumulative_cost { + best_match = candidate; + } + } + } else { + might_still_find_run_matches = false; + } + } + last_seen.insert(key, run_start); + } + + if let Some(candidate) = Token::new(predecessor, 0, length) { + if candidate.cumulative_cost <= best_match.cumulative_cost { + best_match = candidate; + } + } + } + assert!(best_match.cumulative_cost != MAX_COST); + tokens.push(best_match); + } + + let mut result = Vec::::new(); + let mut endp = src.len(); + while endp > 0 { + let next = tokens.swap_remove(endp); + endp -= next.length; + result.push(next); + } + assert!(endp == 0); + result.reverse(); + result +} + +mod encoding_costs { + fn log2(mut v: usize) -> usize { + let mut r = if v > 0xFFFF { 1usize << 4 } else { 0 }; + v >>= r; + let mut shift = if v > 0xFF { 1usize << 3 } else { 0 }; + v >>= shift; + r |= shift; + shift = if v > 0xF { 1usize << 2 } else { 0 }; + v >>= shift; + r |= shift; + shift = if v > 0x3 { 1usize << 1 } else { 0 }; + v >>= shift; + r |= shift; + r | (v >> 1) + } + + pub fn egl_k(i: usize, k: usize) -> usize { + let i = i >> k; + log2(i + 1) * 2 + 1 + k + } + + pub fn new_ofse(i: usize) -> usize { + let upper = i >> 8; + let bits_for_upper = egl_k(upper, 0); + + if i < 256 { + bits_for_upper + egl_k(i & 255, 3) + } else { + bits_for_upper + 8 + } + } +} + +struct BitStreamCursor { + bits_written: usize, + bits_index: usize, + debug_str: String, +} + +impl BitStreamCursor { + fn new(debug_str: &str) -> BitStreamCursor { + BitStreamCursor { + bits_written: 8, + bits_index: 0, + debug_str: debug_str.to_string(), + } + } +} + +struct Encoder { + encoded_stream: Vec, + single: BitStreamCursor, + pairs: BitStreamCursor, + nibbles: BitStreamCursor, + + last_symbol_was_copy: bool, + first_symbol: bool, + target_addr: u16, + reverse: bool, + + bit_count: usize, + // purely for instrumenting encode costs + log: Option, + start_addr: u16, +} + +impl Encoder { + fn new(log: Option, start_address: u16, reverse: bool) -> Encoder { + Encoder { + encoded_stream: Vec::new(), + single: BitStreamCursor::new(" "), + pairs: BitStreamCursor::new("\\/"), + nibbles: BitStreamCursor::new("|T||"), + last_symbol_was_copy: false, + first_symbol: true, + target_addr: 0, + reverse: reverse, + bit_count: 0, + log: log, + start_addr: start_address, + } + } + fn _push_byte(&mut self, b: u8) { + self.encoded_stream.push(b); + } + fn push_byte(&mut self, b: u8) { + optlog!( + self.log, + "{:04x} : {:02x}", + self.encoded_stream.len() + (self.start_addr as usize), + b + ); + self._push_byte(b); + self.bit_count += 8; + } + fn push_word(&mut self, w: u16) { + self.push_byte((w & 0xff) as u8); + self.push_byte((w >> 8) as u8); + } + fn push_bit(&mut self, b: u8) { + self.push_bit_to_stream(b, 1) + } + fn push_pair_bit(&mut self, b: u8) { + self.push_bit_to_stream(b, 2) + } + //fn push_nibble_bit (&mut self, b:u8) { self.push_bit_to_stream(b, 4) } + + fn push_bit_to_stream(&mut self, b: u8, sid: usize) { + let ref mut stream = match sid { + 1 => &mut self.single, + 2 => &mut self.pairs, + 4 => &mut self.nibbles, + _ => panic!("undefined stream"), + }; + + if stream.bits_written == 8 { + stream.bits_index = self.encoded_stream.len(); + self.encoded_stream.push(b << 7); + stream.bits_written = 1; + } else { + self.encoded_stream[stream.bits_index] += b * (128 >> stream.bits_written); + stream.bits_written += 1; + } + optlog!( + self.log, + "{:04x}.{}: {:}{:x}", + stream.bits_index + (self.start_addr as usize), + 8 - stream.bits_written, + &stream.debug_str[{ + let j = stream.bits_written % stream.debug_str.len(); + j..j + 1 + }], + b + ); + self.bit_count += 1; + } + + fn push_interleaved_exp_golom_k( + &mut self, + x: u16, + k: i32, + all_pairs: bool, + nibble_wrapped: bool, + ) { + let remainder = x % (1 << k); + let mut head = (x >> k) + 1; + let mut bits: Vec = Vec::new(); + while head > 0 { + bits.push((head % 2) as u8); + head = head >> 1; + } + bits.pop(); //last 1 is implicit + + let headstream = if nibble_wrapped { + 4 + } else if all_pairs { + 2 + } else { + 1 + }; + + if bits.len() == 0 { + self.push_bit_to_stream(0, headstream); + } else { + self.push_bit_to_stream(1, headstream); + + while let Some(nextbit) = bits.pop() { + self.push_pair_bit(nextbit); + self.push_pair_bit(if bits.len() > 0 { 1 } else { 0 }); + } + } + if nibble_wrapped { + assert!(k % 4 == 3) + } + for i in 0..k { + let stream = if nibble_wrapped { + 4 + } else if i < (k - k % 2) || all_pairs { + 2 + } else { + 1 + }; + self.push_bit_to_stream(((remainder >> (k - i - 1)) % 2) as u8, stream); + } + } + + fn new_segment(&mut self, target_addr: u16) { + self.push_word(target_addr); + optlog!(self.log, "Segment address: {:04x}", target_addr); + self.first_symbol = true; + self.last_symbol_was_copy = false; + self.target_addr = target_addr; + } + fn encode_literal(&mut self, data: &[u8]) { + if self.last_symbol_was_copy { + self.push_bit(0); + } else { + if !self.first_symbol { + let target_addr = self.target_addr; //preserve what target was before ivar is disturbed by the marker encoding + self.encode_copy(256, 2); //length 256 to terminate segment, offset of 2 to continue/1 to stop + self.new_segment(target_addr); + } + } + let length = data.len(); + assert!(length < 256); + self.push_interleaved_exp_golom_k((length - 1) as u16, 0, false, false); + for d in data { + self.push_byte(*d); + } + optlog!( + self.log, + " {:04x} ({:02x} {:04x}) {:}", + self.target_addr, + length, + 0, + { + let hd: Vec = data.iter().map(|x| format!("{:02x}", x)).collect(); + hd.join(" ") + } + ); + self.last_symbol_was_copy = false; + self.first_symbol = false; + if self.reverse { + self.target_addr -= length as u16; + } else { + self.target_addr += length as u16; + } + } + fn encode_copy(&mut self, length: usize, offset: usize) { + if self.last_symbol_was_copy { + self.push_bit(1); + } + assert!(offset >= 1); + let enc_offset = offset - 1; + let oh = (enc_offset >> 8) as u8; + let ol = (enc_offset & 0xff) as u8; + + self.push_interleaved_exp_golom_k(oh as u16, 0, true, false); + if oh > 0 { + self.push_byte(ol); + } else { + self.push_interleaved_exp_golom_k(ol as u16, 3, false, true); + } + assert!(length >= 2); + self.push_interleaved_exp_golom_k((length - 2) as u16, 0, true, false); + + if length < 256 { + optlog!( + self.log, + " {:04x} ({:02x} {:04x})", + self.target_addr, + length, + offset + ); + } + + self.last_symbol_was_copy = true; + self.first_symbol = false; + if self.reverse { + self.target_addr -= length as u16; + } else { + self.target_addr += length as u16; + } + } +} + +pub fn process(conf: &Conf) -> Result<(), String> { + let mut input_chunk_list = conf.input_chunk_list.clone(); + + input_chunk_list.last_mut().unwrap().end_of_group = true; + + let self_extracting = match conf.load_location { + TargetLocation::SelfExtracting => true, + _ => false, + }; + + let log = conf.log_fn + .clone() + .and_then(|path| match File::create(&path) { + Ok(x) => Some(x), + _ => None, + }); + + let log_addr = match conf.load_location { + TargetLocation::Start(addr) => addr, + TargetLocation::End(_) => 0, + TargetLocation::Auto => 0, + TargetLocation::SelfExtracting => 0, + }; + + let mut e = Encoder::new(None, log_addr, conf.reverse); + + e.log = log; + optlog!(e.log, "producing {}", conf.output_fn); + optlog!(e.log, "Load address: {:04x}", log_addr); + + optprintln!(conf.verbose, ""); + + let group_count = input_chunk_list.iter().filter(|x| x.end_of_group).count(); + if group_count > 1 { + optprintln!( + conf.verbose, + "{} group{}", + group_count, + if group_count == 1 { "" } else { "s" } + ); + } + + if self_extracting { + let any_early_starters = input_chunk_list.iter().any(|x| x.chunk.addr < 0x0800); + if any_early_starters { + return Err( + "self extracting option doesn't currently support destinations below $0800" + .to_owned(), + ); + } + if group_count != 1 { + return Err( + "self extracting option doesn't currently support multiple groups.".to_owned(), + ); + } + optprintln!(conf.verbose, "sorting input chunks"); + + let j = input_chunk_list.len() - 1; + input_chunk_list[j].end_of_group = false; + input_chunk_list.sort_unstable_by_key(|x| 0xffff - x.chunk.addr); + + if conf.zero_fill { + optprintln!(conf.verbose, "infilling with clear areas"); + let mut start_of_next = input_chunk_list[0].chunk.addr; + for c in input_chunk_list[1..].iter_mut() { + let ea = c.chunk.end_addr(); + if ea < start_of_next { + for _ in ea..start_of_next { + c.chunk.data.push(0); + } + } + start_of_next = c.chunk.addr; + } + } + + optprintln!(conf.verbose, "merging input chunks"); + let mut merged_chunks = vec![input_chunk_list[0].clone()]; + for c in input_chunk_list[1..].iter() { + let mut c = c.clone(); + let psa = merged_chunks.last().unwrap().chunk.addr; + let cea = c.chunk.end_addr(); + if psa == cea { + { + let pred = &merged_chunks.last().unwrap().chunk; + c.chunk.data.extend(pred.data.clone()); + } + merged_chunks.pop(); + } + merged_chunks.push(c.clone()); + } + input_chunk_list = merged_chunks; + + if conf.set_eob { + fn lo(addr: u16) -> u8 { + (addr & 0xff) as u8 + }; + fn hi(addr: u16) -> u8 { + ((addr >> 8) & 0xff) as u8 + }; + let end_addr = input_chunk_list.last().unwrap().chunk.end_addr(); + optprintln!( + conf.verbose, + "adding chunk to set 0x2d/2e to 0x{:04x}", + end_addr + ); + input_chunk_list.insert( + 0, + InputChunk { + chunk: DataChunk { + data: vec![lo(end_addr), hi(end_addr)], + addr: 0x002d, + }, + end_of_group: false, + }, + ); + } + + { + let j = input_chunk_list.len() - 1; + input_chunk_list[j].end_of_group = true; + } + optprintln!(conf.verbose, ""); + } + + let mut suggested_start_address = 0; + let mut suggested_end_address = 0xffff; + let mut first_in_group = true; + for input_spec in input_chunk_list.iter() { + if first_in_group { + optprintln!(conf.verbose, "starting group"); + first_in_group = false; + } + + let chunk = &input_spec.chunk; + let mut buffer = chunk.data.clone(); + + optprint!( + conf.verbose, + "0x{:04x}-0x{:04x}: {:5} bytes", + chunk.addr, + chunk.end_addr(), + buffer.len() + ); + + let enc_target_addr = if conf.reverse { + buffer.reverse(); + chunk.addr as usize + buffer.len() - 1 + } else { + chunk.addr as usize + }; + e.new_segment(enc_target_addr as u16); + + let tokens = crunch(&buffer); + + let pre_encode_len = e.encoded_stream.len(); + let mut encoded_bytes = 0; + for t in tokens { + if t.offset == 0 { + let literal = &buffer[encoded_bytes..encoded_bytes + t.length]; + optlog!(e.log, "encoding literal token ({} bytes)", t.length); + e.encode_literal(literal); + } else { + optlog!( + e.log, + "encoding copy token (length {}, offset {})", + t.length, + t.offset + ); + e.encode_copy(t.length, t.offset); + } + + encoded_bytes += t.length; + + // at this point, file has taken len(e.encoded_stream) bytes to encode encoded_bytes bytes from current chunk, + if !conf.reverse { + // forward stream; need to ensure + // compressed_start+e.encoded_stream.len()>=enc_target_addr+encoded_bytes + // compressed_start>=enc_target_addr+encoded_bytes-e.encoded_stream.len() + if suggested_start_address + e.encoded_stream.len() + < encoded_bytes + enc_target_addr + { + suggested_start_address = + encoded_bytes + enc_target_addr - e.encoded_stream.len(); + } + } else { + // reversed stream, must ensure + // compressed_end-e.encoded_stream.len()<=enc_target_addr-encoded_bytes + // compressed_end<=enc_target_addr-encoded_bytes+e.encoded_stream.len() + if suggested_end_address + encoded_bytes > enc_target_addr + e.encoded_stream.len() + { + assert!(enc_target_addr > encoded_bytes); + suggested_end_address = + enc_target_addr - encoded_bytes + e.encoded_stream.len(); + } + } + } + + if input_spec.end_of_group { + e.encode_copy(256, 1); //length 256 to terminate segment, offset of 2 to continue/1 to stop + } else { + e.encode_copy(256, 2); //length 256 to terminate segment, offset of 2 to continue/1 to stop + } + let bytes_produced = e.encoded_stream.len() - pre_encode_len; + + if bytes_produced <= buffer.len() { + optprintln!( + conf.verbose, + " compressed to {:5} bytes ({:5.1}% smaller)", + bytes_produced, + (buffer.len() as f32 - bytes_produced as f32) * 100f32 / (buffer.len() as f32) + ); + } else { + optprintln!( + conf.verbose, + " expanded to {:5} bytes ({:5.1}% bigger)", + bytes_produced, + (bytes_produced as f32 - buffer.len() as f32) * 100f32 / (buffer.len() as f32) + ); + } + + if !conf.reverse { + let suggested_end_address = (suggested_start_address + e.encoded_stream.len()) as u16; + optprintln!( + conf.verbose && !self_extracting, + "lowest usable end address now 0x{:04x}, 0x{:04x} bytes past end of chunk", + suggested_end_address, + suggested_end_address as usize - chunk.end_addr() as usize + ); + } else { + let suggested_start_address = + (suggested_end_address - (e.encoded_stream.len() - 1)) as u16; + optprintln!( + conf.verbose && !self_extracting, + "highest usable start address now 0x{:04x}, 0x{:04x} bytes before start of chunk", + suggested_start_address, + chunk.addr as usize - suggested_start_address as usize + ); + } + if input_spec.end_of_group { + optprintln!(conf.verbose, "ending group\n"); + first_in_group = true; + } + } + + let load_addr = match conf.load_location { + TargetLocation::Start(addr) => addr, + TargetLocation::End(e_addr) => e_addr - (e.encoded_stream.len() as u16), + TargetLocation::Auto => { + (if conf.reverse { + suggested_end_address - (e.encoded_stream.len() - 1) + } else { + suggested_start_address + }) as u16 + } + TargetLocation::SelfExtracting => 0x0801, + }; + if conf.reverse { + optprintln!(conf.verbose, "reversing stream"); + e.encoded_stream.reverse(); + } + if let TargetLocation::SelfExtracting = conf.load_location { + optprintln!(conf.verbose, "prepending boot"); + e.encoded_stream = boot::patch_and_prepend_boot( + &e.encoded_stream, + conf.exec_addr, + conf.memory_config, + conf.compact, + conf.sei, + ); + } + write_prg( + &conf.output_fn[..], + &DataChunk { + addr: load_addr, + data: e.encoded_stream, // no need to clone, as we're done with the encoder now. + }, + ).map_err(|e| format!("Could not write {} - {}", &conf.output_fn, e)) +} diff --git a/loader/tools/nucrunch-1.0.1/src/main.rs b/loader/tools/nucrunch-1.0.1/src/main.rs new file mode 100644 index 0000000..db45d09 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/src/main.rs @@ -0,0 +1,237 @@ +/* + * NuCrunch 1.0 + * Christopher Jam + * May 2018 + */ +#[macro_use] +extern crate clap; +extern crate nucrunch; + +use clap::{App, Arg, ArgGroup}; +use nucrunch::{process, read_prg, Conf, InputChunk, TargetLocation}; +use std::process; + +struct InputSpec { + pub file_name: String, + pub end_of_group: bool, +} + +fn load_input_chunks(file_names: Vec<&str>) -> Vec { + let mut input_fn_list: Vec = file_names + .iter() + .map(|x: &&str| { + let mut x = x.to_string(); + match x.pop().unwrap() { + ',' => InputSpec { + file_name: x, + end_of_group: true, + }, + e => { + x.push(e); + InputSpec { + file_name: x, + end_of_group: false, + } + } + } + }) + .collect(); + match input_fn_list.pop() { + None => unreachable!(), + Some(group_end) => input_fn_list.push(InputSpec { + file_name: group_end.file_name, + end_of_group: true, + }), + } + input_fn_list + .iter() + .map(|x| InputChunk { + chunk: match read_prg(&x.file_name) { + Ok(x) => x, + Err(e) => { + eprintln!("Could not read {} ({})", x.file_name, e); + process::exit(1); + } + }, + end_of_group: x.end_of_group, + }) + .collect() +} + +fn parse_u16(arg: &str) -> Result { + if arg.len() > 2 && &arg[..2] == "0x" { + u16::from_str_radix(&arg[2..], 16) + .map_err(|_| format!("Invalid hex string in arguments ('{}')", arg)) + } else { + u16::from_str_radix(arg, 10) + .map_err(|_| format!("could not parse number in arguments ('{}')", arg)) + } +} + +fn build_app<'a, 'b>() -> App<'a, 'b> { + App::new("nucrunch") + .version(crate_version!()) + .author("Christopher Phillips ") + .arg( + Arg::with_name("inputs") + .multiple(true) + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("output") + .short("o") + .long("output") + .value_name("OUTPUT.PRG") + .help("sets filename for output .prg") + .required(true), + ) + .arg( + Arg::with_name("exec_addr") + .short("j") + .long("jmp") + .value_name("0xEXEC_ADDR") + .requires("sea") + .help("execution address for self extracting .prg (requires -x)"), + ) + .arg( + Arg::with_name("memory_config") + .short("m") + .long("memory_config") + .value_name("0xCFG") + .requires("sea") + .help("0x01 value post decrunch (default 0x37, requires -x)"), + ) + .arg( + Arg::with_name("zero_fill") + .short("z") + .long("zero_fill") + .requires("sea") + .help("zero fills the areas between chunks (requires -x)"), + ) + .arg( + Arg::with_name("sei") + .short("I") + .long("sei") + .requires("sea") + .help("wrap decrunch in SEI/CLI instead of turning off CIA timer (requires -x)"), + ) + .arg( + Arg::with_name("set_eob") + .short("b") + .long("set_eob") + .requires("sea") + .help("sets the eof pointer at 2d/2e to end of highest input chunk (requires -x)"), + ) + .arg( + Arg::with_name("sea") + .short("x") + .long("sea") + .help("produce self extracting output .prg (implies -r)"), + ) + .arg( + Arg::with_name("load") + .short("l") + .long("load") + .value_name("LOAD_ADDRESS") + .help("sets load address for raw output .prg"), + ) + .arg( + Arg::with_name("end") + .short("e") + .long("end") + .value_name("END_ADDRESS") + .help("sets end address for raw output .prg"), + ) + .arg( + Arg::with_name("auto") + .short("A") + .long("auto") + // .conflicts_with("reverse") // this didn't work - created clash with entire + // group + .help("determines minimal overhang location for raw output .prg."), + ) + .group( + ArgGroup::with_name("target") + .args(&["sea", "load", "end", "auto"]) + .required(true), + ) + .arg( + Arg::with_name("reverse") + .short("r") + .long("reverse") + //.conflicts_with("auto") + .help("output for rdecrunch (unpacks from high to low)"), + ) + .arg( + Arg::with_name("compact") + .short("c") + .long("compact") + .help("selects slightly smaller and slower decruncher for sea") + .requires("sea"), + ) + .arg(Arg::with_name("verbose").short("v").long("verbose")) + .arg( + Arg::with_name("log") + .short("L") + .long("log") + .value_name("LOG_FILE") + .help("log all the tokens produced"), + ) + .after_help( + " + eg for a simple repack to a self extracting .prg + nucrunch -cbxo output.prg onefileinput.prg + + Use commas to delineate groups, eg + nucrunch f1g1.prg f2g1.prg, f1g2.prg, f1g3,prg f2g3.prg f3g3.prg -o out.prg -l 0x1000 + + Call decrunch to unpack the first group, then decrunch_next_group for each subsequent group", + ) +} + +fn parse_args(app: App) -> Result { + let matches = app.get_matches(); + + let file_names: Vec<&str> = matches.values_of("inputs").unwrap().collect(); + let input_chunk_list = load_input_chunks(file_names); + + let load_location = match ( + matches.is_present("sea"), + matches.is_present("load"), + matches.is_present("end"), + matches.is_present("auto"), + ) { + (true, _, _, _) => TargetLocation::SelfExtracting, + (_, true, _, _) => TargetLocation::Start(parse_u16(&matches.value_of("load").unwrap())?), + (_, _, true, _) => TargetLocation::End(parse_u16(&matches.value_of("end").unwrap())?), + (_, _, _, true) => TargetLocation::Auto, + _ => unreachable!(), + }; + + Ok(Conf { + input_chunk_list, + output_fn: matches.value_of("output").unwrap().to_string(), + reverse: matches.is_present("reverse") || load_location == TargetLocation::SelfExtracting, + zero_fill: matches.is_present("zero_fill"), + sei: matches.is_present("sei"), + set_eob: matches.is_present("set_eob"), + load_location, + compact: matches.is_present("compact"), + log_fn: matches.value_of("log").and_then(|x| Some(x.to_string())), + verbose: matches.is_present("verbose"), + memory_config: parse_u16(matches.value_of("memory_config").unwrap_or("0x37"))? as u8, + exec_addr: parse_u16(matches.value_of("exec_addr").unwrap_or("0x080d"))?, + }) +} + +fn main() { + let app = build_app(); + match parse_args(app).and_then(|conf| process(&conf)) { + Err(e) => { + eprintln!("error: {}", e); + process::exit(1); + } + Ok(()) => {} + }; +} diff --git a/loader/tools/nucrunch-1.0.1/srdecrunch.s b/loader/tools/nucrunch-1.0.1/srdecrunch.s new file mode 100644 index 0000000..3ca7258 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/srdecrunch.s @@ -0,0 +1,271 @@ +; +; NuCrunch 0.1 +; Christopher Jam +; February 2016 +; + +; next byte is fetched from mem[ mem[zps+1]*256+(mem[zps]-1)%256 ] +; ie, init pointer to start byte then increment low byte of pointer. +; Low byte is decremented before each fetch, then if it hits zero the high byte is predecremented for the next fetch +; note that getByte requires y to be zero +; also note that this mangled pointer is a tad inimical to the literal fetches -/ +; +; This is the compact version with much less aggressive inlining. Will be slower. + +.export decrunch_next_group +.export decrunch +.export decrunch_end + + + +.define getByte1 jsr get_byte + + +.macro getBit1 + jsr get_bit_1 +.endmacro + + + +; get head of a pair of bits from the bitpair stream +; (must getBit2t precisely once before invoking again) + +.macro getBit2h +.local nomore + asl zbs2 + bne nomore + getByte1 + sec + rol + sta zbs2 +nomore: +.endmacro + +; same, but preserving A/ trashing X. +.macro getBit2hpa + jsr get_bit_2hpa +.endmacro + +.macro getBit2t + asl zbs2 +.endmacro + +; get head of a quad of bits from the quad stream +; (must getBit4t precisely three times before invoking again) + +.macro getBit4h +.local nomore + asl zbs4 + bne nomore + getByte1 + sec + rol + sta zbs4 +nomore: +.endmacro + + +; get tail of a quad of bits from the quad stream +.macro getBit4t + asl zbs4 +.endmacro + +; note, trashes X. Also, carry is clear when done +.macro getExpGoulombTail +.local ndone +ndone: + getBit2hpa + rol + getBit2t + bcs ndone +.endmacro + +.macro getExpGoulombTail_odd_aligned +.local ndone +ndone: + getBit2t + rol + getBit2hpa + bcs ndone +.endmacro + + +decrunch_zpa =$e0 ;6 bytes required + + +zps = decrunch_zpa+$00 +zbs1 = decrunch_zpa+$02 +zbs2 = decrunch_zpa+$03 +zbs4 = decrunch_zpa+$04 +offsetm1h = decrunch_zpa+$05 + + + +decrunch: + + stx zps+0 + sta zps+1 + ldy #0 + sty zbs1 + sty zbs2 + sty zbs4 + + +decrunch_next_group: + ldy #0 +next_segment: + jsr get_byte + sta copy_dst+1 + jsr get_byte + sta copy_dst+2 + +decode_literal: + +getEG0p1: ;get count [eg0+1] in x + ldx#1 + getBit1 + lda#1 + bcc ret1 + lda#1 + getExpGoulombTail + tax +ret1: + txa + sec + eor#255 + adc copy_dst+1 + sta copy_dst+1 + sta literal_dst+1 + bcs *+5 + dec copy_dst+2 + lda copy_dst+2 + sta literal_dst+2 + + + +literal_loop: +literal_src: + getByte1 +literal_dst: + sta $f000,x + dex + bne literal_loop + + + ; literal is always followed by copy + +decode_copy: + getBit2h + bcc short_offset + lda#1 + getExpGoulombTail_odd_aligned + adc#255 + sta offsetm1h + getByte1 + jmp got_high + +short_offset: + lda#0 + sta offsetm1h + + ;ExpGoulomb k=3 + getBit4h + lda#1 + bcc no_tail + getExpGoulombTail_odd_aligned +no_tail: + adc#255 + + getBit4t + rol + getBit4t + rol + getBit4t + rol +got_high: + pha + + ldx#2 + getBit2t + bcc length_two + lda#1 + getExpGoulombTail + tax + inx + beq end_of_segment ; copy length of 256 marks end of segment +length_two: + + txa + eor#255 + sec + adc copy_dst+1 + sta copy_dst+1 + bcs *+6 + dec copy_dst+2 + sec + + ; note carry is set at this point; good as we want to add (offsetm1+1) + pla + adc copy_dst+1 + sta copy_src+1 + + lda copy_dst+2 + adc offsetm1h + sta copy_src+2 + +copy_loop: +copy_src: + lda $f000,x +copy_dst: + sta $f000,x + dex + bne copy_loop + + + ldy#0 + getBit1 + bcs jmp_decode_copy + jmp decode_literal +jmp_decode_copy: + jmp decode_copy + +get_bit_1: + asl zbs1 + bne :+ + getByte1 + sec + rol + sta zbs1 +: + rts +get_bit_2hpa: + asl zbs2 + bne :+ + tax + getByte1 + sec + rol + sta zbs2 + txa +: rts + +get_byte: + dec zps+0 + beq load_decrement_return +load_and_return: + lda (zps),y + rts + +load_decrement_return: + lda (zps),y + dec zps+1 + +end_of_file: + rts + +end_of_segment: + pla + cmp#0 + beq end_of_file + jmp next_segment + diff --git a/loader/tools/nucrunch-1.0.1/test/Makefile b/loader/tools/nucrunch-1.0.1/test/Makefile new file mode 100644 index 0000000..77cf221 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/test/Makefile @@ -0,0 +1,50 @@ +CRUNCH = ../target/release/nucrunch +RUNNER = x64sc + +all: go.prg rgo.prg go_ca65.prg + +run: go.prg + $(RUNNER) $< + +rrun: rgo.prg + $(RUNNER) $< + +crun: go_ca65.prg + $(RUNNER) $< + +go.prg: testbed.prg crunchedbmp.prg + ./cbmcat $^ >$@ + +rgo.prg: rtestbed.prg rcrunchedbmp.prg + ./cbmcat $^ >$@ + +go_ca65.prg: testbed_ca65.prg crunchedbmp.prg + ./cbmcat $^ >$@ + +rcrunchedbmp.prg: bmp0.prg bmp1.prg bmp_c.prg $(CRUNCH) Makefile + #$(CRUNCH) bmp1.prg bmp0.prg, bmp_c.prg -e 0x2000 -r -o $@ -L lr.log -v + $(CRUNCH) bmp1.prg bmp0.prg, bmp_c.prg --auto -r -o $@ -L lr.log -v + +crunchedbmp.prg: bmp0.prg bmp1.prg bmp_c.prg $(CRUNCH) Makefile + #$(CRUNCH) bmp1.prg bmp0.prg, bmp_c.prg -l 0x1000 -o $@ -L lf.log -v + $(CRUNCH) bmp1.prg bmp0.prg, bmp_c.prg --auto -o $@ -L lf.log -v + +crunched_addr.gen: crunchedbmp.prg + echo "decrunch_src = `./startaddr crunchedbmp.prg`" >$@ +rcrunched_addr.gen: rcrunchedbmp.prg + echo "decrunch_src = `./endaddr rcrunchedbmp.prg`" >$@ + +bmp.prg bmp0.prg bmp1.prg bmp_c.prg: gentest.py crc8.py + python $< + +testbed.prg: ../decrunch.a65 crunched_addr.gen +rtestbed.prg: ../rdecrunch.a65 rcrunched_addr.gen + +testbed_ca65.prg: testbed_ca65.s ../decrunch.s crunched_addr.gen + cl65 -C c64-asm.cfg -u __EXEHDR__ --cpu 6502x -o $@ testbed_ca65.s ../decrunch.s + +%.prg: %.a65 + xa $< -o $@ -l $@.lst -DNUCRUNCH_ALIGN_FOR_SPEED + +clean: + $(RM) -rf *.prg *.prg.lst __pycache__ *.pyc l*.log *.gen diff --git a/loader/tools/nucrunch-1.0.1/test/cbmcat b/loader/tools/nucrunch-1.0.1/test/cbmcat new file mode 100644 index 0000000..46795f7 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/test/cbmcat @@ -0,0 +1,45 @@ +#!/usr/bin/env python +from __future__ import print_function +import sys +from struct import pack,unpack + +if sys.platform == "win32": + import os, msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + +class Mem: + def __init__(self): + self.low_water=0xffff + self.high_water=0 + page=b'\0'*256 + self.mem=page*256 + def load_prg(self, fn): + data=open(fn,'rb').read() + start=unpack('(decrunch_src-1) + jsr decrunch + jsr crc9k + ldx#50 +wdata + bit$d011 + bpl *-3 + bit$d011 + bmi *-3 + dex + bne wdata + stx $d020 + jsr decrunch_next_group + jsr crc9k +st jmp st + + + +crc9k + .( + ldy#$40 + ldx#0 + stx crc +hbl + sty crcl+2 +crcl + lda $4000,x + jsr crc8 + inx + bne crcl + iny + cpy #$64 + bne hbl + + ldy #5 + cmp #0 + beq ok + ldy #2 +ok sty $d020 + rts + .) + + +crc8: ; h/t Greg Cook, 6502.org/source/integers/crc-more.html + .( + eor crc ; A contained the data + sta crc ; XOR it with the byte + asl ; current contents of A will become x^2 term + bcc up1 ; if b7 = 1 + eor #$07 ; then apply polynomial with feedback +up1 eor crc ; apply x^1 + asl ; C contains b7 ^ b6 + bcc up2 + eor #$07 +up2 eor crc ; apply unity term + sta crc ; save result + rts + .) +crc brk + +#include "../rdecrunch.a65" diff --git a/loader/tools/nucrunch-1.0.1/test/sea_test.s b/loader/tools/nucrunch-1.0.1/test/sea_test.s new file mode 100644 index 0000000..4feb6bb --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/test/sea_test.s @@ -0,0 +1,14 @@ + + .org $0810-2 + .word *+2 + lda#$3b + sta $d011 + lda#$18 + sta $d018 + lda#0 + sta $d020 + lda #5 + sta $d021 + jsr $e536 + jmp * + diff --git a/loader/tools/nucrunch-1.0.1/test/startaddr b/loader/tools/nucrunch-1.0.1/test/startaddr new file mode 100644 index 0000000..8c18355 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/test/startaddr @@ -0,0 +1,6 @@ +#!/usr/bin/python +from __future__ import print_function +from sys import argv +from struct import unpack +addr=unpack('decrunch_src + jsr decrunch + jsr crc9k + ldx#50 +wdata + bit$d011 + bpl *-3 + bit$d011 + bmi *-3 + dex + bne wdata + stx $d020 + jsr decrunch_next_group + jsr crc9k +st jmp st + + + +crc9k + .( + ldy#$40 + ldx#0 + stx crc +hbl + sty crcl+2 +crcl + lda $4000,x + jsr crc8 + inx + bne crcl + iny + cpy #$64 + bne hbl + + ldy #5 + cmp #0 + beq ok + ldy #2 +ok sty $d020 + rts + .) + + +crc8: ; h/t Greg Cook, 6502.org/source/integers/crc-more.html + .( + eor crc ; A contained the data + sta crc ; XOR it with the byte + asl ; current contents of A will become x^2 term + bcc up1 ; if b7 = 1 + eor #$07 ; then apply polynomial with feedback +up1 eor crc ; apply x^1 + asl ; C contains b7 ^ b6 + bcc up2 + eor #$07 +up2 eor crc ; apply unity term + sta crc ; save result + rts + .) +crc brk + +#include "../decrunch.a65" diff --git a/loader/tools/nucrunch-1.0.1/test/testbed_ca65.s b/loader/tools/nucrunch-1.0.1/test/testbed_ca65.s new file mode 100644 index 0000000..46fc6d1 --- /dev/null +++ b/loader/tools/nucrunch-1.0.1/test/testbed_ca65.s @@ -0,0 +1,74 @@ +.include "crunched_addr.gen" +.import decrunch +.import decrunch_next_group + + lda #150 + sta $dd00 + lda #59 + sta $d011 + lda #$80 + sta $d018 + lda #6 + sta $d020 + ldx #decrunch_src + jsr decrunch + jsr crc9k + ldx#50 +wdata: + bit$d011 + bpl *-3 + bit$d011 + bmi *-3 + dex + bne wdata + stx $d020 + jsr decrunch_next_group + jsr crc9k +st: + jmp st + + + +crc9k: + ldy#$40 + ldx#0 + stx crc +hbl: + sty crcl+2 +crcl: + lda $4000,x + jsr crc8 + inx + bne crcl + iny + cpy #$64 + bne hbl + + ldy #5 + cmp #0 + beq ok + ldy #2 +ok: + sty $d020 + rts + + +crc8: ; h/t Greg Cook, 6502.org/source/integers/crc-more.html + eor crc ; A contained the data + sta crc ; XOR it with the byte + asl ; current contents of A will become x^2 term + bcc up1 ; if b7 = 1 + eor #$07 ; then apply polynomial with feedback +up1: + eor crc ; apply x^1 + asl ; C contains b7 ^ b6 + bcc up2 + eor #$07 +up2: + eor crc ; apply unity term + sta crc ; save result + rts +crc: + brk + diff --git a/loader/tools/pucrunch/Makefile b/loader/tools/pucrunch/Makefile new file mode 100644 index 0000000..f7282f8 --- /dev/null +++ b/loader/tools/pucrunch/Makefile @@ -0,0 +1,4 @@ +all: pucrunch + +pucrunch: pucrunch.c pucrunch.h + gcc -Wall -funsigned-char pucrunch.c -o pucrunch -O -lm -Dstricmp=strcasecmp diff --git a/loader/tools/pucrunch/cbmcombine.c b/loader/tools/pucrunch/cbmcombine.c new file mode 100644 index 0000000..f3ad1b3 --- /dev/null +++ b/loader/tools/pucrunch/cbmcombine.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +typedef unsigned char UBYTE; + +static far UBYTE mem[65536]; + +int main(int argc,char *argv[]) +{ + int i=0, low=65535, hi=0; + FILE *handle; + UBYTE la[2]; + + if(argc<2) + { + fprintf(stderr,"Usage: %s [input-file [,load-address] ]*\n", + argv[0]); + return 5; + } + memset(mem, sizeof(mem), 0); + for(i=1;i>8); + fwrite(la, sizeof(UBYTE), 2, stdout); + fwrite(mem + low, sizeof(UBYTE), hi-low, stdout); + return 0; +} + + diff --git a/loader/tools/pucrunch/pucrunch.c b/loader/tools/pucrunch/pucrunch.c new file mode 100644 index 0000000..7ab19c3 --- /dev/null +++ b/loader/tools/pucrunch/pucrunch.c @@ -0,0 +1,3419 @@ +#include +#include +#include +#include +#include +#include + + + +#define DELTA +#define DELTA_OP + + + +/* Pucrunch ©1997-2008 by Pasi 'Albert' Ojala, a1bert@iki.fi */ +/* Pucrunch is now under LGPL: see the doc for details. */ + + +/* #define BIG */ +/* + Define BIG for >64k files. + It will use even more *huge* amounts of memory. + + Note: + Although this version uses memory proportionally to the file length, + it is possible to use fixed-size buffers. The LZ77 history buffer + (and backSkip) needs to be as long as is needed, the other buffers + minimally need to be about three times the length of the maximum + LZ77 match. Writing the compressor this way would probably make it a + little slower, and automatic selection of e.g. escape bits might not be + practical. + + Adjusting the number of escape bits to adapt to local + changes in the data would be worth investigating. + + Also, the memory needed for rle/elr tables could probably be reduced + by using a sparse table implementation. Because of the RLE property + only the starting and ending points (or lengths) need be saved. The + speed should not decrease too much, because the tables are used in + LZ77 string match also.... Wait! Actually no, because the RLE/LZ77 + optimize needs to change the RLE lengths inside RLE's... + + The elr array can be reduced to half by storing only the byte that + is before a run of bytes if we have the full backSkip table.. + + Because the lzlen maximum value is 256, we could reduce the table + from unsigned short to unsigned char by encoding 0->0, 2->1, .. 256->255. + lzlen of the value 1 is never used anyway.. + + */ + +#define ENABLE_VERBOSE /* -v outputs the lz77/rle data to stdout */ +#define HASH_STAT /* gives statistics about the hash compares */ +#define BACKSKIP_FULL /* full backSkip table - enables RESCAN. If */ + /* not defined, backSkip only uses max 128kB */ +#define RESCAN /* rescans LZ77 matches for a closer match. */ +#define HASH_COMPARE /* Use a 3-to-1 hash to skip impossible matches */ +/* takes "inbytes" bytes, reduces string compares from 16% to 8% */ + + + +const char version[] = "\0$VER: pucrunch 1.14 22-Nov-2008\n"; + + + +static int maxGamma = 7, reservedBytes = 2; +static int escBits = 2, escMask = 0xc0; +static int extraLZPosBits = 0, rleUsed = 15; + +static int memConfig = 0x37, intConfig = 0x58; /* cli */ + + +/* +--------> + z..zx.....x normal (zz != ee) + e..e value(LEN) value(POSHI+1) 8+b(POSLO) LZ77 + e..e 0 (2) 0 (2-256) 8b(POSLO) LZ77 + e..e 100 (3) 111111 111111 END of FILE +#ifdef DELTA + e..e 101 (4..) 111111 111111 8b(add) 8b(POSLO) DLZ +#endif + + e..e010 n..ne.....e escape + new esc + e..e011 value(LEN) bytecode Short RLE 2.. + e..e011 111..111 8b(LENLO) value(LENHI+1) bytecode Long RLE + (values 64.. not used (may not be available) in bytecode) + + +e..e011 0 0 RLE=2, rank 1 (saves 11.. bit) +e..e011 0 10 x RLE=2, rank 2-3 (saves 9.. bit) +e..e011 0 11 0xx RLE=2, rank 4-7 (saves 7.. bit) +e..e011 0 11 10xxx RLE=2, rank 8-15 (saves 5.. bit) +e..e011 0 11 110xxxx xxxx RLE=2, not ranked + + +LZ77, len=2 (pos<=256) saves 4 bits (2-bit escape) +LZ77, len=3 saves 10..1 bits (pos 2..15616) +LZ77, len=4 saves 18..9 bits +LZ77, len=5 saves 24..15 bits + +RLE, len=2 saves 11..1(..-5) bits (bytecode rank 1..not ranked) +RLE, len=3 saves 15..2 bits +RLE, len=4 saves 23..10 bits +RLE, len=5 saves 29..16 bits + +bs: 3505 LZ reference points, 41535 bytes -> 11.85, i.e. 8.4% referenced + + + 1) Short RLE -> gamma + 1 linear bit -> ivanova.run -29 bytes + + 2) ?? .. no + esc = RLE, with value 1 + e..e01 value(1) n..ne.....e escape + new esc + e..e01 value(LEN) bytecode Short RLE 2.. + e..e01 111..111 8b(LENLO) value(LENHI+1) bytecode Long RLE + (values 64.. not used (may not be available) in bytecode) + + +*/ + +/* +Value: + +Elias Gamma Code rediscovered, just the prefix bits are reversed, plus +there is a length limit (1 bit gained for each value in the last group) +; 0000000 not possible +; 0000001 0 1 -6 bits +; 000001x 10 x 2-3 -4 bits +; 00001xx 110 xx 4-7 -2 bits +; 0001xxx 1110 xxx 8-15 +0 bits +; 001xxxx 11110 xxxx 16-31 +2 bits +; 01xxxxx 111110 xxxxx 32-63 +4 bits +; 1xxxxxx 111111 xxxxxx 64-127 +5 bits + +*/ + + +#include "pucrunch.h" /* Include the decompressors */ + + +void ListDecompressors(FILE *fp) { + struct FixStruct *dc = &fixStruct[0]; + + while (dc && dc->code) { + fprintf(fp, "%s\n", dc->name); + dc++; + } +} + +struct FixStruct *BestMatch(int type) { + struct FixStruct *dc = &fixStruct[0], *best = NULL; + + while (dc && dc->code) { + if ((dc->flags & FIXF_MACHMASK) == (type & FIXF_MACHMASK)) { + /* machine is correct */ + /* Require wrap if necessary, allow wrap if not */ + /* Require delta matches */ + if (((dc->flags & type) & FIXF_MUSTMASK) == + (type & FIXF_MUSTMASK)) { + + /* Haven't found any match or this is better */ + if (!best || + ((type & FIXF_WRAP) == (dc->flags & FIXF_WRAP) && + (!(type & (FIXF_FAST | FIXF_SHORT)) || + (dc->flags & type & (FIXF_FAST | FIXF_SHORT))))) + best = dc; + /* If requirements match exactly, can return */ + /* Assumes that non-wraps are located before wrap versions */ + if ((type & (FIXF_FAST | FIXF_SHORT)) == + (dc->flags & (FIXF_FAST | FIXF_SHORT))) { + return dc; + } + } + } + dc++; + } + return best; +} + + +int GetHeaderSize(int type, int *deCall) { + struct FixStruct *best; + if (deCall) + *deCall = 0; + if ((type & FIXF_MACHMASK) == 0) { + return 47; /* standalone */ + } + best = BestMatch(type); + if (best && deCall) { + int i; + for (i=0; best->fixes[i].type != ftEnd; i++) { + if (best->fixes[i].type == ftDeCall) { + *deCall = best->fixes[i].offset; + break; + } + } + } + return best?best->codeSize:0; +} + + +int SavePack(int type, unsigned char *data, int size, char *target, + int start, int exec, int escape, unsigned char *rleValues, + int endAddr, int progEnd, int extraLZPosBits, int enable2MHz, + int memStart, int memEnd) { + FILE *fp = NULL; + struct FixStruct *dc; + unsigned char *header; + int i, overlap = 0, stackUsed = 0, ibufferUsed = 0; + + if (!data) + return 10; + if (!target) + fp = stdout; + + if ((type & FIXF_MACHMASK) == 0) { + /* Save without decompressor */ + + if (fp || (fp = fopen(target, "wb"))) { + unsigned char head[64]; + int cnt = 0; + + head[cnt++] = (endAddr + overlap - size) & 0xff; /* INPOS */ + head[cnt++] = ((endAddr + overlap - size) >> 8); + + head[cnt++] = 'p'; + head[cnt++] = 'u'; + + head[cnt++] = (endAddr - 0x100) & 0xff; + head[cnt++] = ((endAddr - 0x100) >> 8); + + head[cnt++] = (escape>>(8-escBits)); + head[cnt++] = (start & 0xff); /* OUTPOS */ + head[cnt++] = (start >> 8); + head[cnt++] = escBits; + /* head[cnt++] = 8-escBits; */ + + head[cnt++] = maxGamma + 1; + /* head[cnt++] = (8-maxGamma); */ /* Long RLE */ + head[cnt++] = (1<> 8); + + head[cnt++] = rleUsed; + for(i = 1; i <= rleUsed; i++) { + head[cnt++] = rleValues[i]; + } + + fwrite(head, 1, cnt, fp); + fwrite(data, size, 1, fp); + if(fp != stdout) + fclose(fp); + return 0; + } + fprintf(stderr, "Could not open %s for writing\n", target); + return 10; + } + if ((memStart & 0xff) != 1) { + fprintf(stderr, "Misaligned basic start 0x%04x\n", memStart); + return 10; + } else if (memStart > 9999) { + /* The basic line only holds 4 digits.. */ + fprintf(stderr, "Too high basic start 0x%04x\n", memStart); + return 10; + } + + if (endAddr > memEnd) { + overlap = endAddr - memEnd; + endAddr = memEnd; + + /* + Make the decrunch code wrap from $ffff to $004b. + The decrunch code first copies the data that would exceed + $ffff to $004b and then copy the rest of it to end at $ffff. + */ + + if (overlap > 22) { + fprintf(stderr, "Warning: data overlap is %d, but only 22 " + "is totally safe!\n", overlap); + fprintf(stderr, "The data from $61 to $%02x is overwritten.\n", + 0x4b + overlap); + } + } + if (overlap) { + type |= FIXF_WRAP; + } else { + type &= ~FIXF_WRAP; + } + dc = BestMatch(type); + if (!dc) { + fprintf(stderr, "No matching decompressor found\n"); + return 10; + } + header = dc->code; + + if (!memStart) + memStart = 0x801; +#ifndef BIG + if (memStart + dc->codeSize - 2 + size > 0xfe00) { + fprintf(stderr, "Packed file's max size is 0x%04x (0x%04x)!\n", + 0xfe00-memStart-(dc->codeSize-2), size); + return 10; + } +#endif /* BIG */ + + for (i=0; dc->fixes[i].type != ftEnd; i++) { + switch (dc->fixes[i].type) { + case ftFastDisable: + if (!enable2MHz) { + header[dc->fixes[i].offset] = 0x2c; + } + break; + case ftOverlap: + header[dc->fixes[i].offset] = overlap ? (overlap-1) : 0; + break; + case ftOverlapLo: + header[dc->fixes[i].offset] = + (memStart+dc->codeSize-2+rleUsed-15+size - overlap) & 0xff; + break; + case ftOverlapHi: + header[dc->fixes[i].offset] = + (memStart+dc->codeSize-2+rleUsed-15+size - overlap) >> 8; + break; + case ftWrapCount: + header[dc->fixes[i].offset] = + (memEnd>>8) - ((endAddr + overlap - size) >> 8); /* wrap point.. */ + break; + + case ftSizePages: + header[dc->fixes[i].offset] = (size>>8) + 1; + break; + case ftSizeLo: + header[dc->fixes[i].offset] = + (memStart+dc->codeSize-2+rleUsed-15+size-0x100 - overlap) & 0xff; + break; + case ftSizeHi: + header[dc->fixes[i].offset] = + (memStart+dc->codeSize-2+rleUsed-15+size-0x100 - overlap) >> 8; + break; + case ftEndLo: + header[dc->fixes[i].offset] = (endAddr - 0x100) & 0xff; + break; + case ftEndHi: + header[dc->fixes[i].offset] = ((endAddr - 0x100) >> 8); + break; + case ftEscValue: + header[dc->fixes[i].offset] = (escape>>(8-escBits)); + break; + case ftOutposLo: + header[dc->fixes[i].offset] = (start & 0xff); /* OUTPOS */ + break; + case ftOutposHi: + header[dc->fixes[i].offset] = (start >> 8); + break; + case ftEscBits: + header[dc->fixes[i].offset] = escBits; + break; + case ftEsc8Bits: + header[dc->fixes[i].offset] = 8-escBits; + break; + case ft1MaxGamma: + header[dc->fixes[i].offset] = (1<fixes[i].offset] = (8-maxGamma); /* Long RLE */ + break; + case ft2MaxGamma: + header[dc->fixes[i].offset] = (2<fixes[i].offset] = extraLZPosBits; + break; + case ftMemConfig: + header[dc->fixes[i].offset] = memConfig; + break; + case ftCli: + header[dc->fixes[i].offset] = intConfig; /* $58/$78 cli/sei; */ + break; + case ftExecLo: + header[dc->fixes[i].offset] = (exec & 0xff); + break; + case ftExecHi: + header[dc->fixes[i].offset] = (exec >> 8); + break; + case ftInposLo: + header[dc->fixes[i].offset] = (endAddr + overlap - size) & 0xff; /* INPOS */ + break; + case ftInposHi: + header[dc->fixes[i].offset] = ((endAddr + overlap - size) >> 8); + break; + case ftMaxGamma: + header[dc->fixes[i].offset] = maxGamma + 1; + break; + case ftReloc: + if (header[1] != (memStart>>8)) { + header[dc->fixes[i].offset] -= (header[1] - (memStart >> 8)); + } + break; + + case ftBEndLo: + header[dc->fixes[i].offset] = (progEnd & 0xff); + break; + case ftBEndHi: + header[dc->fixes[i].offset] = (progEnd >> 8); + break; + + case ftStackSize: + stackUsed = header[dc->fixes[i].offset]; + break; + case ftIBufferSize: + ibufferUsed = header[dc->fixes[i].offset]; + break; + + default: + break; + } + } + + for (i=1; i<=15; i++) + header[dc->codeSize - 15 + i-1] = rleValues[i]; + + if (header[1] != (memStart>>8)) { + header[1] = (memStart>>8); /* Load address */ + header[3] = (memStart>>8); /* Line link */ + + header[7] = 0x30 + (memStart+12)/1000; + header[8] = 0x30 + ((memStart+12)/100 % 10); + header[9] = 0x30 + ((memStart+12)/10 % 10); + header[10] = 0x30 + ((memStart+12) % 10); + } + + fprintf(stderr, "Saving %s\n", dc->name); + if (fp || (fp = fopen(target, "wb"))) { + fwrite(header, 1, dc->codeSize+rleUsed-15, fp); + fwrite(data, size, 1, fp); + if (fp != stdout) + fclose(fp); + } else { + fprintf(stderr, "Could not open %s for writing\n", target); + return 10; + } + if (dc->flags & FIXF_SHORT) { + fprintf(stderr, "%s uses the memory $2d-$30, ", target?target:""); + } else { + fprintf(stderr, "%s uses the memory $2d/$2e, ", target?target:""); + } + if (overlap) + fprintf(stderr, "$4b-$%02x, ", 0x4b + overlap); + else if ((dc->flags & FIXF_WRAP)) + fprintf(stderr, "$4b, "); + if (stackUsed) + fprintf(stderr, "$f7-$%x, ", 0xf7 + stackUsed); + if (ibufferUsed) + fprintf(stderr, "$200-$%x, ", 0x200 + ibufferUsed); + fprintf(stderr, "and $%04x-$%04x.\n", + (start < memStart+1) ? start : memStart+1, endAddr-1); + return 0; +} + + + +#ifdef ENABLE_VERBOSE +#define F_VERBOSE (1<<0) +#endif +#define F_STATS (1<<1) +#define F_AUTO (1<<2) +#define F_NOOPT (1<<3) +#define F_AUTOEX (1<<4) +#define F_SKIP (1<<5) +#define F_2MHZ (1<<6) +#define F_AVOID (1<<7) +#define F_DELTA (1<<8) + +#define F_NORLE (1<<9) + +#define F_UNPACK (1<<14) +#define F_ERROR (1<<15) + +#ifndef min +#define min(a,b) ((a 1..127 */ +#define MAXLZLEN (2< 1..127 */ +#define DEFAULT_LZLEN LRANGE + +static int lrange, maxlzlen, maxrlelen; + + + +#ifdef BIG +#define OUT_SIZE 2000000 +#else +#define OUT_SIZE 65536 +#endif /* BIG */ +static unsigned char outBuffer[OUT_SIZE]; +static int outPointer = 0; +static int bitMask = 0x80; + + +static void FlushBits(void) { + if (bitMask != 0x80) + outPointer++; +} + + +static void PutBit(int bit) { + if (bit && outPointer < OUT_SIZE) + outBuffer[outPointer] |= bitMask; + bitMask >>= 1; + if (!bitMask) { + bitMask = 0x80; + outPointer++; + } +} + + +void PutValue(int value) { + int bits = 0, count = 0; + + while (value>1) { + bits = (bits<<1) | (value & 1); /* is reversed compared to value */ + value >>= 1; + count++; + PutBit(1); + } + if (count same as value */ + bits >>= 1; + } +} + +#if 0 +int LenValue(int value) { + int count = 0; + + while (value>1) { + value >>= 1; + count += 2; + } + if (count>(8-escBits)), escBits); /* escBits>=0 */ + PutValue(2-1); + PutBit(1); + PutBit(0); + +#if 0 + *esc = (*esc + (1<<(8-escBits))) & escMask; + PutNBits(data[0], 8-escBits); + + gainedEscaped += 3; +#else + *esc = newesc; + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + PutNBits(data[0], 8-escBits); + + gainedEscaped += escBits + 3; +#endif + timesEscaped++; + return 1; + } + PutNBits(data[0], 8); + return 0; +} + + + +void OutputEof(int *esc); +void OutputEof(int *esc) { + /* EOF marker */ + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + PutValue(3-1); /* >1 */ + PutValue((2<>4/*3*/)); + + gainedRlecode -= LenValue(16/*32*/+(data>>4/*3*/)) + 4/*3*/; + + PutNBits(data, 4/*3*/); + + lenStat[4/*5*/][3]++; + /* Note: values 64..127 are not used if maxGamma>5 */ +} + + +#if 0 +int LenRleByte(unsigned char data) { + int index; + + for (index = 1; index < 16/*32*/; index++) { + if (data == rleValues[index]) { + return LenValue(index); + } + } + return LenValue(16/*32*/ + 0) + 4/*3*/; +} +#else +static unsigned char rleLen[256]; +void InitRleLen(void); +void InitRleLen() { + int i; + + for (i=0; i<256; i++) + rleLen[i] = LenValue(16/*32*/ + 0) + 4/*3*/; + for (i=1; i<16 /*32*/; i++) + rleLen[rleValues[i]] = LenValue(i); +} +#define LenRleByte(d) (rleLen[d]) +#endif + + +int LenRle(int len, int data) { + int out = 0; + + do { + if (len == 1) { + out += escBits + 3 + 8; + len = 0; + } else if (len <= (1<>8)+1) + LenRleByte(data); + + len -= tmp; + } + } while (len); + return out; +} + +int OutputRle(int *esc, unsigned char *data, int rlelen) { + int len = rlelen, tmp; + + while (len) { + if (len >= 2 && len <= (1<>(8-escBits)), escBits); /* escBits>=0 */ + PutValue(2-1); + PutBit(1); + PutBit(1); + PutValue(len-1); + PutRleByte(*data); + + tmp = 8*len -escBits -3 -LenValue(len-1) -LenRleByte(*data); + gainedRle += tmp; + gainedSRle += tmp; + + timesRle++; + timesSRle++; + return 0; + } + if (len<3) { + while (len--) + OutputNormal(esc, data, *esc); + return 0; + } + + if (len <= maxrlelen) { + /* Run-length encoding */ + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + + PutValue(2-1); + PutBit(1); + PutBit(1); + + PutValue((1<>(8-maxGamma))); + + PutNBits((len-1), 8-maxGamma); + PutValue(((len-1)>>8) + 1); + PutRleByte(*data); + + tmp = 8*len -escBits -3 -maxGamma -8 -LenValue(((len-1)>>8)+1) + -LenRleByte(*data); + gainedRle += tmp; + gainedLRle += tmp; + + timesRle++; + timesLRle++; + return 0; + } + + /* Run-length encoding */ + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + + PutValue(2-1); + PutBit(1); + PutBit(1); + + PutValue((1<>(8-maxGamma))); + + PutNBits((maxrlelen-1) & 0xff, 8-maxGamma); + PutValue(((maxrlelen-1)>>8)+1); + PutRleByte(*data); + + tmp = 8*maxrlelen -escBits -3 -maxGamma -8 + -LenValue(((maxrlelen-1)>>8)+1) -LenRleByte(*data); + gainedRle += tmp; + gainedLRle += tmp; + timesRle++; + timesLRle++; + len -= maxrlelen; + data += maxrlelen; + } + return 0; +} + + +#ifdef DELTA +/* e..e 101 (4..) 111111 111111 8b(add) 8b(POSLO) DLZ*/ +static int LenDLz(int lzlen, int lzpos) { + return escBits + 2*maxGamma + 8 + 8 + LenValue(lzlen-1); +} + +static int OutputDLz(int *esc, int lzlen, int lzpos, int add) { + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + + PutValue(lzlen-1); + PutValue((2<>(8+extraLZPosBits))+1) + + LenValue(lzlen-1); +} + + +static int OutputLz(int *esc, int lzlen, int lzpos, char *data, int curpos) { + if (lzlen==2) + lenStat[0][1]++; + else if (lzlen<=4) + lenStat[1][1]++; + else if (lzlen<=8) + lenStat[2][1]++; + else if (lzlen<=16) + lenStat[3][1]++; + else if (lzlen<=32) + lenStat[4][1]++; + else if (lzlen<=64) + lenStat[5][1]++; + else if (lzlen<=128) + lenStat[6][1]++; + else if (lzlen<=256) + lenStat[7][1]++; + + if (lzlen >= 2 && lzlen <= maxlzlen) { + int tmp; + + PutNBits((*esc>>(8-escBits)), escBits); /* escBits>=0 */ + + tmp = ((lzpos-1)>>(8+extraLZPosBits))+2; + if (tmp==2) + lenStat[0][0]++; + else if (tmp<=4) + lenStat[1][0]++; + else if (tmp<=8) + lenStat[2][0]++; + else if (tmp<=16) + lenStat[3][0]++; + else if (tmp<=32) + lenStat[4][0]++; + else if (tmp<=64) + lenStat[5][0]++; + else if (tmp<=128) + lenStat[6][0]++; + else if (tmp<=256) + lenStat[6][0]++; + + if (lzlen==2) { + PutValue(lzlen-1); + PutBit(0); + if (lzpos > 256) + fprintf(stderr, + "Error at %d: lzpos too long (%d) for lzlen==2\n", + curpos, lzpos); +#if 0 + if (lzpos <= 16) { + PutBit(0); + PutNBits(((lzpos-1) & 0xff) ^ 0xff, 4); + } else { + PutBit(1); + PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8); + } +#else + PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8); +#endif + } else { + PutValue(lzlen-1); + PutValue( ((lzpos-1) >> (8+extraLZPosBits)) +1); + PutNBits( ((lzpos-1) >> 8), extraLZPosBits); + PutNBits(((lzpos-1) & 0xff) ^ 0xff, 8); + } + + gainedLz += 8*lzlen -LenLz(lzlen, lzpos); + timesLz++; + return 3; + } + fprintf(stderr, "Error: lzlen too short/long (%d)\n", lzlen); + return lzlen; +} + + + +static unsigned short *rle, *elr, *lzlen, *lzpos, *lzmlen, *lzmpos; +#ifdef DELTA +static unsigned short *lzlen2, *lzpos2; +#endif +static int *length, inlen; +static unsigned char *indata, *mode, *newesc; +unsigned short *backSkip; + + +enum MODE { + LITERAL = 0, + LZ77 = 1, + RLE = 2, +#ifdef DELTA + DLZ = 3, +#endif + MMARK = 4 +}; + +static int lzopt = 0; +/* Non-recursive version */ +/* NOTE! IMPORTANT! the "length" array length must be inlen+1 */ + +int OptimizeLength(int optimize) { + int i; + + length[inlen] = 0; /* one off the end, our 'target' */ + for (i=inlen-1; i>=0; i--) { + int r1 = 8 + length[i+1], r2, r3; + + if (!lzlen[i] && !rle[i] +#ifdef DELTA + && (!lzlen2 || !lzlen2[i]) +#endif + ) { + length[i] = r1; + mode[i] = LITERAL; + continue; + } + + /* If rle>maxlzlen, skip to the start of the rle-maxlzlen.. */ + if (rle[i] > maxlzlen && elr[i] > 1) { + int z = elr[i]; + + i -= elr[i]; + + r2 = LenRle(rle[i], indata[i]) + length[i+ rle[i]]; + if (optimize) { + int ii, mini = rle[i], minv = r2; + + int bot = rle[i] - (1<=bot; ii--) { + int v = LenRle(ii, indata[i]) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + } + } + if (minv != r2) { + lzopt += r2 - minv; + rle[i] = mini; + r2 = minv; + } + } + length[i] = r2; + mode[i] = RLE; + + for (; z>=0; z--) { + length[i+z] = r2; + mode[i+z] = RLE; + } + continue; + } + r3 = r2 = r1 + 1000; /* r3 >= r2 > r1 */ + + if (rle[i]) { + r2 = LenRle(rle[i], indata[i]) + length[i+ rle[i]]; + + if (optimize) { + int ii, mini = rle[i], minv = r2; + +#if 0 + int bot = rle[i] - (1<=bot; ii--) { + int v = LenRle(ii, indata[i]) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + } + } +#else + /* Check only the original length and all shorter + lengths that are power of two. + + Does not really miss many 'minimums' this way, + at least not globally.. + + Makes the assumption that the Elias Gamma Code is + used, i.e. values of the form 2^n are 'optimal' */ + ii = 2; + while (rle[i] > ii) { + int v = LenRle(ii, indata[i]) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + } + ii <<= 1; + } +#endif + if (minv != r2) { +/*printf("%05d RL %d %d\n", i, rle[i], mini);*/ + lzopt += r2 - minv; + rle[i] = mini; + r2 = minv; + } + } + } + if (lzlen[i]) { + r3 = LenLz(lzlen[i], lzpos[i]) + length[i + lzlen[i]]; + + if (optimize && lzlen[i]>2) { + int ii, mini = lzlen[i], minv = r3, mino = lzpos[i]; + int topLen = LenLz(lzlen[i], lzpos[i]) + - LenValue(lzlen[i]-1); + +#if 0 + int bot = 3; + if (lzpos[i] <= 256) + bot = 2; + + for (ii=mini-1; ii>=bot; ii--) { + int v = topLen + LenValue(ii-1) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + } + } +#else + /* Check only the original length and all shorter + lengths that are power of two. + + Does not really miss many 'minimums' this way, + at least not globally.. + + Makes the assumption that the Elias Gamma Code is + used, i.e. values of the form 2^n are 'optimal' */ + ii = 4; + while (lzlen[i] > ii) { + int v = topLen + LenValue(ii-1) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + } + ii <<= 1; + } + + /* Then check the max lengths we have found, but + did not originally approve because they seemed + to gain less than the shorter, nearer matches. */ + ii = 3; + while (lzmlen[i] >= ii) { + int v = LenLz(ii, lzmpos[i]) + length[i + ii]; + if (v < minv) { + minv = v; + mini = ii; + mino = lzmpos[i]; + } + ii++; + } +#endif +#ifdef BACKSKIP_FULL + /* + Note: + 2-byte optimization checks are no longer done + with the rest, because the equation gives too long + code lengths for 2-byte matches if extraLzPosBits>0. + */ + /* Two-byte rescan/check */ + if (backSkip[i] && backSkip[i] <= 256) { + /* There are previous occurrances (near enough) */ + int v = LenLz(2, (int)backSkip[i]) + length[i + 2]; + + if (v < minv) { + minv = v; + mini = 2; + lzlen[i] = mini; + r3 = minv; + lzpos[i] = (int)backSkip[i]; + } + } +#endif /* BACKSKIP_FULL */ + if (minv != r3 && minv < r2) { + /* + printf("@%05d LZ %d %4x -> %d %4x\n", + i, lzlen[i], lzpos[i], mini, lzpos[i]); + */ + lzopt += r3 - minv; + lzlen[i] = mini; + lzpos[i] = mino; + r3 = minv; + } + } + } + + if (r2 <= r1) { + if (r2 <= r3) { + length[i] = r2; + mode[i] = RLE; + } else { + length[i] = r3; + mode[i] = LZ77; + } + } else { + if (r3 <= r1) { + length[i] = r3; + mode[i] = LZ77; + } else { + length[i] = r1; + mode[i] = LITERAL; + } + } +#ifdef DELTA + if (lzlen2 && lzlen2[i] > 3) { + r3 = LenDLz(lzlen2[i], lzpos2[i]) + length[i + lzlen2[i]]; + if (r3 < length[i]) { + length[i] = r3; + mode[i] = DLZ; + } + } +#endif + } + return length[0]; +} + + +/* + The algorithm in the OptimizeEscape() works as follows: + 1) Only unpacked bytes are processed, they are marked + with MMARK. We proceed from the end to the beginning. + Variable A (old/new length) is updated. + 2) At each unpacked byte, one and only one possible + escape matches. A new escape code must be selected + for this case. The optimal selection is the one which + provides the shortest number of escapes to the end + of the file, + i.e. A[esc] = 1+min(A[0], A[1], .. A[states-1]). + For other states A[esc] = A[esc]; + If we change escape in this byte, the new escape is + the one with the smallest value in A. + 3) The starting escape is selected from the possibilities + and mode 0 is restored to all mode 3 locations. + + */ + +int OptimizeEscape(int *startEscape, int *nonNormal) { + int i, j, states = (1<256) { + fprintf(stderr, "Escape optimize: only 256 states (%d)!\n", + states); + return 0; + } + + /* Mark those bytes that are actually outputted */ + for (i=0; i=0; i--) { + /* Using a table to skip non-normal bytes does not help.. */ + if (mode[i] == MMARK) { + int k = (indata[i] >> esc8); + + /* Change the tag values back to normal */ + mode[i] = LITERAL; + + /* + k are the matching bytes, + minv is the minimum value, + minp is the minimum index + */ + + newesc[i] = (minp << esc8); + a[k] = minv + 1; + b[k] = b[minp] + 1; + if (k==minp) { + /* Minimum changed -> need to find a new minimum */ + /* a[k] may still be the minimum */ + minv++; + for (k=states-1; k>=0; k--) { + if (a[k] < minv) { + minv = a[k]; + minp = k; + /* + There may be others, but the first one that + is smaller than the old minimum is equal to + any other new minimum. + */ + break; + } + } + } + } + } + + /* Select the best value for the initial escape */ + if (startEscape) { + i = inlen; /* make it big enough */ + for (j=states-1; j>=0; j--) { + if (a[j] <= i) { + *startEscape = (j << esc8); + i = a[j]; + } + } + } + if (nonNormal) + *nonNormal = other; + return b[startEscape ? (*startEscape>>esc8) : 0]; +} + + +/* Initialize the RLE byte code table according to all RLE's found so far */ +/* O(n) */ +void InitRle(int); +void InitRle(int flags) { + int p, mr, mv, i; + + for (i=1; i<16/*32*/; i++) { + mr = -1; + mv = 0; + + for (p=0; p<256; p++) { + if (rleHist[p] > mv) { + mv = rleHist[p]; + mr = p; + } + } + if (mv>0) { + rleValues[i] = mr; + rleHist[mr] = -1; + } else + break; + } + InitRleLen(); +} + + +/* Initialize the RLE byte code table according to RLE's actually used */ +/* O(n) */ +void OptimizeRle(int); +void OptimizeRle(int flags) { + int p, mr, mv, i; + + if ((flags & F_NORLE)) { + rleUsed = 0; + return; + } + if ((flags & F_STATS)) + fprintf(stderr, "RLE Byte Code Re-Tune, RLE Ranks:\n"); + for (p=0; p<256; p++) + rleHist[p] = 0; + + for (p=0; p mv) { + mv = rleHist[p]; + mr = p; + } + } + if (mv>0) { + rleValues[i] = mr; + if ((flags & F_STATS)) { + fprintf(stderr, " %2d.0x%02x %-3d ", i, mr, mv); + if (!((i - 1) % 6)) + fprintf(stderr, "\n"); + } + rleHist[mr] = -1; + } else + break; + } + rleUsed = i-1; + + if ((flags & F_STATS)) + if (((i - 1) % 6)!=1) + fprintf(stderr, "\n"); + InitRleLen(); +} + + +static const unsigned char *up_Data; +static int up_Mask, up_Byte; +void up_SetInput(const unsigned char *data) { + up_Data = data; + up_Mask = 0x80; + up_Byte = 0; +} +int up_GetBits(int bits) { + int val = 0; + + while (bits--) { + val <<= 1; + if ((*up_Data & up_Mask)) + val |= 1; + up_Mask >>= 1; + if (!up_Mask) { + up_Mask = 0x80; + up_Data++; + up_Byte++; + } + } + return val; +} +int up_GetValue(void) { + int i = 0; + + while (i 8) { + fprintf(stderr, "Error: Broken archive, escBits %d.\n", + escBits); + return 20; + } + maxGamma = data[cnt++] - 1; + if (data[cnt++] != (1< 7) { + fprintf(stderr, "Error: Broken archive, maxGamma %d.\n", + maxGamma); + return 20; + } + lrange = LRANGE; + maxlzlen = MAXLZLEN; + maxrlelen = MAXRLELEN; + + extraLZPosBits = data[cnt++]; + if (extraLZPosBits < 0 || extraLZPosBits > 4) { + fprintf(stderr, "Error: Broken archive, extraLZPosBits %d.\n", + extraLZPosBits); + return 20; + } + + execAddr = data[cnt] | (data[cnt+1]<<8); + cnt += 2; + + rleUsed = data[cnt++]; + byteCodeVec = (const char *) &data[cnt - 1]; + + overlap = 0; + //memConfig = memConfig; + //intConfig = intConfig; + + size = endAddr-startAddr; + headerSize = cnt + rleUsed; + + endAddr = loadAddr + size; + + } else { + + for (i=0; fixStruct[i].code && i < MAXCODES; i++) { + int j, maxDiff = 0; + + if (fixStruct[i].code[1] != (loadAddr>>8)) + maxDiff = 5; + for (j=0; fixStruct[i].fixes[j].type != ftEnd; j++) { + maxDiff++; + } + mismatch[i] = 0; + for (j=2; j %d)\n", + fixStruct[i].name, mismatch[i], maxDiff); + } + dc = &fixStruct[i]; + if (!dc->code) { + fprintf(stderr, + "Error: The file is not compressed with this program.\n"); + return 20; + } + + if ((loadAddr & 0xff) != 1) { + fprintf(stderr, "Error: Misaligned basic start address 0x%04x\n", + loadAddr); + return 20; + } + /* TODO: check that the decrunch code and load address match. */ + + error = 0; + + for (i=0; ifixes[i].type!=ftEnd; i++) { + collect[dc->fixes[i].type] = data[dc->fixes[i].offset-2]; + } + + overlap = collect[ftOverlap]; + /* TODO: check overlap LO/HI and WrapCount */ + maxGamma = collect[ftMaxGamma] - 1; + if (maxGamma < 5 || maxGamma > 7) { + fprintf(stderr, "Error: Broken archive, maxGamma %d.\n", + maxGamma); + return 20; + } + lrange = LRANGE; + maxlzlen = MAXLZLEN; + maxrlelen = MAXRLELEN; + + if (collect[ft1MaxGamma] != (1< 8) { + fprintf(stderr, "Error: Broken archive, escBits %d.\n", + escBits); + return 20; + } + + if (collect[ftEsc8Bits] != 8-escBits) { + fprintf(stderr, "Error: Broken archive, escBits (%d) mismatch.\n", + escBits); + return 20; + } + + extraLZPosBits = collect[ftExtraBits]; + if (extraLZPosBits < 0 || extraLZPosBits > 4) { + fprintf(stderr, "Error: Broken archive, extraLZPosBits %d.\n", + extraLZPosBits); + return 20; + } + endAddr = 0x100 + (collect[ftEndLo] | (collect[ftEndHi]<<8)); + size = endAddr - (collect[ftInposLo] | (collect[ftInposHi]<<8)); + headerSize = ((collect[ftSizeLo] | (collect[ftSizeHi]<<8)) + + 0x100 - size - loadAddr) & 0xffff; + execAddr = collect[ftExecLo] | (collect[ftExecHi]<<8); + + memConfig = collect[ftMemConfig]; + intConfig = collect[ftCli]; + byteCodeVec = (const char *) &data[dc->codeSize - 32 -2]; + + rleUsed = 15 - dc->codeSize +2 + headerSize; + } + + + if ((flags & F_STATS)) { + fprintf(stderr, + "Load 0x%04x, Start 0x%04lx, exec 0x%04lx, %s%s$01=$%02x\n", + loadAddr, startAddr, execAddr, + (intConfig==0x58)?"cli, ":"", (intConfig==0x78)?"sei, ":"", + memConfig); + fprintf(stderr, "Escape bits %d, starting escape 0x%02lx\n", + escBits, (startEsc<<(8-escBits))); + fprintf(stderr, + "Decompressor size %ld, max length %d, LZPOS LO bits %d\n", + headerSize+2, (2< 15) { + fprintf(stderr, "Error: Old archive, rleUsed %d > 15.\n", rleUsed); + return 20; + } + + outPointer = 0; + up_SetInput(data + headerSize); + while (!error) { + int sel = startEsc; + +#ifndef BIG + if (startAddr + outPointer >= up_Byte + endAddr - size) { + if (!error) + fprintf(stderr, "Error: Target %5ld exceeds source %5ld..\n", + startAddr + outPointer, up_Byte + endAddr - size); + error++; + } + if (up_Byte > size + overlap) { + fprintf(stderr, "Error: No EOF symbol found (%d > %d).\n", + up_Byte, (int) (size + overlap)); + error++; + } +#endif /* BIG */ + + if (escBits) + sel = up_GetBits(escBits); + if (sel == startEsc) { + int lzPos, lzLen = up_GetValue(), i; +#ifdef DELTA + int add = 0; +#endif + + if (lzLen != 1) { + int lzPosHi = up_GetValue()-1, lzPosLo; + + if (lzPosHi == (2< 2) { + add = up_GetBits(8); + lzPos = up_GetBits(8) ^ 0xff; + } else +#endif + break; /* EOF */ + } else { + if (extraLZPosBits) { + lzPosHi = (lzPosHi<= OUT_SIZE) { + fprintf(stderr, "Error: Broken archive, " + "output buffer overrun at %d.\n", + outPointer); + return 20; + } + continue; + } + rleLen = up_GetValue(); + if (rleLen >= (1<= OUT_SIZE) { + fprintf(stderr, "Error: Broken archive, " + "output buffer overrun at %d.\n", + OUT_SIZE); + return 20; + } + for (i=0; i<=rleLen; i++) { + outBuffer[outPointer++] = byte; + } + continue; + } + lzPos = up_GetBits(8) ^ 0xff; + } +/*fprintf(stdout, "%5ld %5ld LZ %3d 0x%04x\n", + outPointer, up_Byte, lzLen+1, lzPos+1);*/ + + /* outPointer increases in the loop, thus its minimum is here */ + if (outPointer - lzPos -1 < 0) { + fprintf(stderr, "Error: Broken archive, " + "LZ copy position underrun at %d (%d). " + "lzLen %d.\n", + outPointer, lzPos+1, lzLen+1); + return 20; + } + if (outPointer + lzLen + 1 >= OUT_SIZE) { + fprintf(stderr, "Error: Broken archive, " + "output buffer overrun at %d.\n", + OUT_SIZE); + return 20; + } + for (i=0; i<=lzLen; i++) { + outBuffer[outPointer] = outBuffer[outPointer - lzPos - 1] +#ifdef DELTA + DELTA_OP add; +#else + ; +#endif + outPointer++; + } + } else { + int byte = (sel<<(8-escBits)) | up_GetBits(8-escBits); +/*fprintf(stdout, "%5ld %5ld %02x\n", + outPointer, up_Byte, byte);*/ + outBuffer[outPointer++] = byte; + if (outPointer >= OUT_SIZE) { + fprintf(stderr, "Error: Broken archive, " + "output buffer overrun at %d.\n", + outPointer); + return 20; + } + } + } + if (error) + fprintf(stderr, "Error: Target exceeded source %5ld times.\n", + error); + + if ((file && (fp = fopen(file, "wb"))) || (fp = stdout)) { + unsigned char tmp[2]; + tmp[0] = startAddr & 0xff; + tmp[1] = (startAddr >> 8); + + fwrite(tmp, 2, 1, fp); + fwrite(outBuffer, outPointer, 1, fp); + if (fp != stdout) + fclose(fp); + + timeused = clock() - timeused; + if (!timeused) + timeused++; /* round upwards */ + fprintf(stderr, + "Decompressed %d bytes in %4.2f seconds (%4.2f kB/s)\n", + outPointer, + (double)timeused/CLOCKS_PER_SEC, + (double)CLOCKS_PER_SEC*outPointer/timeused/1024.0); + + return error; + } + fprintf(stderr, "Could not open file \"%s\" for writing.\n", file); + return 20; +} + + + +int PackLz77(int lzsz, int flags, int *startEscape, + int endAddr, int memEnd, int type) { + int i, j, outlen, p, headerSize; + int escape; +#ifdef HASH_COMPARE + unsigned char *hashValue; + unsigned char *a; + int k; +#endif /* HASH_COMPARE */ + +#ifdef BIG + unsigned int *lastPair; +#else + unsigned short *lastPair; +#endif /* BIG */ + +#ifdef BACKSKIP_FULL +#ifdef RESCAN + int rescan = 0; +#endif /* RESCAN */ +#endif /* BACKSKIP_FULL */ + +#ifdef HASH_STAT + unsigned long compares = 0, hashChecks = 0, hashEqual = 0; +#endif /* HASH_STAT */ + + if (lzsz < 0 || lzsz > lrange) { + fprintf(stderr, "LZ range must be from 0 to %d (was %d). Set to %d.\n", + lrange, lzsz, lrange); + lzsz = lrange; + } + if (lzsz > 65535) { + fprintf(stderr, + "LZ range must be from 0 to 65535 (was %d). Set to 65535.\n", + lzsz); + lzsz = 65535; + } + if (!lzsz) + fprintf(stderr, "Warning: zero LZ range. Only RLE packing used.\n"); + + InitRleLen(); + length = (int *)calloc(sizeof(int), inlen + 1); + mode = (unsigned char *)calloc(sizeof(unsigned char), inlen); + rle = (unsigned short *)calloc(sizeof(unsigned short), inlen); + elr = (unsigned short *)calloc(sizeof(unsigned short), inlen); + lzlen = (unsigned short *)calloc(sizeof(unsigned short), inlen); + lzpos = (unsigned short *)calloc(sizeof(unsigned short), inlen); + lzmlen = (unsigned short *)calloc(sizeof(unsigned short), inlen); + lzmpos = (unsigned short *)calloc(sizeof(unsigned short), inlen); +#ifdef DELTA + if ((type & FIXF_DLZ)) { + lzlen2 = (unsigned short *)calloc(sizeof(unsigned short), inlen); + lzpos2 = (unsigned short *)calloc(sizeof(unsigned short), inlen); + } else { + lzlen2 = lzpos2 = NULL; + } +#endif + newesc = (unsigned char *)calloc(sizeof(unsigned char), inlen); +#ifdef BACKSKIP_FULL + backSkip = (unsigned short *)calloc(sizeof(unsigned short), inlen); +#else + backSkip = (unsigned short *)calloc(sizeof(unsigned short), 65536); +#endif /* BACKSKIP_FULL */ +#ifdef HASH_COMPARE + hashValue = (unsigned char *)malloc(inlen); +#endif /* HASH_COMPARE */ +#ifdef BIG + lastPair = (unsigned int *)calloc(sizeof(unsigned int), 256*256); +#else + lastPair = (unsigned short *)calloc(sizeof(unsigned short), 256*256); +#endif /* BIG */ + + + /* error checking */ + if (!length || !mode || !rle || !elr || !lzlen || !lzpos || + !lzmlen || !lzmpos || !newesc || !lastPair || !backSkip +#ifdef DELTA + || ((type & FIXF_DLZ) && (!lzlen2 || !lzpos2)) +#endif +#ifdef HASH_COMPARE + || !hashValue +#endif /* HASH_COMPARE */ + ) { + fprintf(stderr, "Memory allocation failed!\n"); + goto errorexit; + } + +#ifdef HASH_COMPARE + i = 0; + j = 0; + a = indata + inlen; + for (p=inlen-1; p>=0; p--) { + k = j; + j = i; + i = *--a; /* Only one read per position */ + + /* Without hash: 18.56%, end+middle: 12.68% */ + /* hashValue[p] = i*2 ^ j*3 ^ k*5; */ /* 8.56% */ + /* hashValue[p] = i ^ j*2 ^ k*3; */ /* 8.85% */ + /* hashValue[p] = i + j + k; */ /* 9.33% */ + /* hashValue[p] = i + j*2 + k*3; */ /* 8.25% */ + /* hashValue[p] = i*2 + j*3 + k*5; */ /* 8.29% */ + /* hashValue[p] = i*3 + j*5 + k*7; */ /* 7.95% */ + hashValue[p] = i*3 + j*5 + k*7; /* 7.95 % */ + } +#endif /* HASH_COMPARE */ + /* Detect all RLE and LZ77 jump possibilities */ + for (p=0; p=2) { + rleHist[indata[p]]++; + + for (i=rlelen-1; i>=0; i--) { + rle[p+i] = rlelen-i; + elr[p+i] = i; /* For RLE backward skipping */ + } +#if 0 + if (rlelen>maxlzlen) { + /* Jump over some unnecessary memory references */ + p += rlelen - maxlzlen - 1; + continue; + } +#endif + } + } + + /* check LZ77 code */ + if (p+rle[p]+1=0 && i>=bot) { + /* Got a 2-byte match at least */ + maxval = 2; + maxpos = p-i; + + /* + A..AB rlep # of A's, B is something else.. + + Search for bytes that are in p + (rlep-1), i.e. + the last rle byte ('A') and the non-matching one + ('B'). When found, check if the rle in the compare + position (i) is long enough (i.e. the same number + of A's at p and i-rlep+1). + + There are dramatically less matches for AB than for + AA, so we get a huge speedup with this approach. + We are still guaranteed to find the most recent + longest match there is. + */ + + i = (int)lastPair[(indata[p+(rlep-1)]<<8) | indata[p+rlep]] -1; + while (i>=bot /* && i>=rlep-1 */) { /* bot>=rlep-1, i>=bot ==> i>=rlep-1 */ + + /* Equal number of A's ? */ + if (!(rlep-1) || rle[i-(rlep-1)]==rlep) { /* 'head' matches */ + /* rlep==1 ==> (rlep-1)==0 */ + /* ivanova.run: 443517 rlep==1, + 709846 rle[i+1-rlep]==rlep */ + + /* + Check the hash values corresponding to the last + two bytes of the currently longest match and + the first new matching(?) byte. If the hash + values don't match, don't bother to check the + data itself. + */ +#ifdef HASH_STAT + hashChecks++; +#endif /* HASH_STAT */ + if ( +#ifdef HASH_COMPARE + hashValue[i+maxval-rlep-1] == hashCompare +#else + indata[i+maxval-rlep+1] == valueCompare +#endif /* HASH_COMPARE */ + ) { + unsigned char *a = indata + i+2; /* match */ + unsigned char *b = indata + p+rlep-1+2;/* curpos */ + int topindex = inlen-(p+rlep-1); + + /* the 2 first bytes ARE the same.. */ + j = 2; + while (j < topindex && *a++==*b++) + j++; + +#ifdef HASH_STAT + hashEqual++; + compares += j - 1; +#endif /* HASH_STAT */ + if (j + rlep-1 > maxval) { + int tmplen = j+rlep-1, tmppos = p-i+rlep-1; + + if (tmplen > maxlzlen) + tmplen = maxlzlen; + + if (lzmlen[p] < tmplen) { + lzmlen[p] = tmplen; + lzmpos[p] = tmppos; + } + /* Accept only versions that really are shorter */ + if (tmplen*8 - LenLz(tmplen, tmppos) > + maxval*8 - LenLz(maxval, maxpos)) { + maxval = tmplen; + maxpos = tmppos; +#ifdef HASH_COMPARE + hashCompare = hashValue[p+maxval-2]; +#else + valueCompare = indata[p+maxval]; +#endif /* HASH_COMPARE */ + } +#if 0 + else { + printf("@%5d %d*8 - LenLz(%d, %4x)==%d < ", + p, tmplen, tmplen, tmppos, + tmplen*8 - LenLz(tmplen, tmppos)); + printf("%d*8 - LenLz(%d, %4x)==%d\n", + maxval, maxval, maxpos, + maxval*8 - LenLz(maxval, maxpos)); + } +#endif + if (maxval == maxlzlen) + break; + } + } + } +#ifdef BACKSKIP_FULL + if (!backSkip[i]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i]; +#else + if (!backSkip[i & 0xffff]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i & 0xffff]; +#endif /* BACKSKIP_FULL */ + } + + /* + If there is 'A' in the previous position also, + RLE-like LZ77 is possible, although rarely + shorter than real RLE. + */ + if (p && rle[p-1] > maxval) { + maxval = rle[p-1] - 1; + maxpos = 1; + } + /* + Last, try to find as long as possible match + for the RLE part only. + */ + if (maxval < maxlzlen && rlep > maxval) { + bot = p - lzsz; + if (bot < 0) + bot = 0; + + /* Note: indata[p] == indata[p+1] */ + i = (int)lastPair[indata[p]*257] -1; + while (/* i>= rlep-2 &&*/ i>=bot) { + if (elr[i] + 2 > maxval) { + maxval = min(elr[i] + 2, rlep); + maxpos = p - i + (maxval-2); + if(maxval == rlep) + break; /* Got enough */ + } + i -= elr[i]; +#ifdef BACKSKIP_FULL + if (!backSkip[i]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i]; +#else + if (!backSkip[i & 0xffff]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i & 0xffff]; +#endif /* BACKSKIP_FULL */ + } + } + if (p+maxval > inlen) { + fprintf(stderr, + "Error @ %d, lzlen %d, pos %d - exceeds inlen\n", + p, maxval, maxpos); + maxval = inlen - p; + } + if (lzmlen[p] < maxval) { + lzmlen[p] = maxval; + lzmpos[p] = maxpos; + } + if (maxpos<=256 || maxval > 2) { + if (maxpos < 0) + fprintf(stderr, "Error @ %d, lzlen %d, pos %d\n", + p, maxval, maxpos); + lzlen[p] = (maxval=0 && i>=bot) { + /* Got a 2-byte match at least */ + maxval = 2; + maxpos = p-i; + + /* + A..AB rlep # of A's, B is something else.. + + Search for bytes that are in p + (rlep-1), i.e. + the last rle byte ('A') and the non-matching one + ('B'). When found, check if the rle in the compare + position (i) is long enough (i.e. the same number + of A's at p and i-rlep+1). + + There are dramatically less matches for AB than for + AA, so we get a huge speedup with this approach. + We are still guaranteed to find the most recent + longest match there is. + */ + + i = (int)lastPair[(((indata[p+(rlep-1)] DELTA_OP rot) & 0xff)<<8) | + ((indata[p+rlep] DELTA_OP rot) & 0xff)] -1; + while (i>=bot /* && i>=rlep-1 */) { /* bot>=rlep-1, i>=bot ==> i>=rlep-1 */ + + /* Equal number of A's ? */ + if (!(rlep-1) || rle[i-(rlep-1)]==rlep) { /* 'head' matches */ + /* rlep==1 ==> (rlep-1)==0 */ + /* ivanova.run: 443517 rlep==1, + 709846 rle[i+1-rlep]==rlep */ + + /* + Check the hash values corresponding to the last + two bytes of the currently longest match and + the first new matching(?) byte. If the hash + values don't match, don't bother to check the + data itself. + */ +#ifdef HASH_STAT + hashChecks++; +#endif /* HASH_STAT */ + if (indata[i+maxval-rlep+1] == valueCompare) { + unsigned char *a = indata + i+2; /* match */ + unsigned char *b = indata + p+rlep-1+2;/* curpos */ + int topindex = inlen-(p+rlep-1); + + /* the 2 first bytes ARE the same.. */ + j = 2; + while (j < topindex && *a++==((*b++ DELTA_OP rot) & 0xff)) + j++; + +#ifdef HASH_STAT + hashEqual++; + compares += j - 1; +#endif /* HASH_STAT */ + if (j + rlep-1 > maxval) { + int tmplen = j+rlep-1, tmppos = p-i+rlep-1; + + if (tmplen > maxlzlen) + tmplen = maxlzlen; + + /* Accept only versions that really are shorter */ + if (tmplen*8 - LenLz(tmplen, tmppos) > + maxval*8 - LenLz(maxval, maxpos)) { + maxval = tmplen; + maxpos = tmppos; + + valueCompare = (indata[p+maxval] DELTA_OP rot) & 0xff; + } +#if 0 + else { + printf("@%5d %d*8 - LenLz(%d, %4x)==%d < ", + p, tmplen, tmplen, tmppos, + tmplen*8 - LenLz(tmplen, tmppos)); + printf("%d*8 - LenLz(%d, %4x)==%d\n", + maxval, maxval, maxpos, + maxval*8 - LenLz(maxval, maxpos)); + } +#endif + if (maxval == maxlzlen) + break; + } + } + } +#ifdef BACKSKIP_FULL + if (!backSkip[i]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i]; +#else + if (!backSkip[i & 0xffff]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i & 0xffff]; +#endif /* BACKSKIP_FULL */ + } + + if (p+maxval > inlen) { + fprintf(stderr, + "Error @ %d, lzlen %d, pos %d - exceeds inlen\n", + p, maxval, maxpos); + maxval = inlen - p; + } + if (maxval > 3 && maxpos <= 256 && + (maxval > lzlen2[p] || + (maxval == lzlen2[p] && maxpos < lzpos2[p]))) { + if (maxpos < 0) + fprintf(stderr, "Error @ %d, lzlen %d, pos %d\n", + p, maxval, maxpos); + lzlen2[p] = (maxval p || ptr > 0xffff) + ptr = 0; + +#ifdef BACKSKIP_FULL + backSkip[p] = ptr; +#else + backSkip[p & 0xffff] = ptr; +#endif /* BACKSKIP_FULL */ + lastPair[index] = p+1; + } + } + if ((flags & F_NORLE)) { + for (p=1; p lzlen[p]) { + lzlen[p] = (rle[p]>escBits) & 0xff; + + /* Find the optimum path for selected escape bits (no optimize) */ + OptimizeLength(0); + + /* Optimize the escape selections for this path & escBits */ + escaped = OptimizeEscape(&escape, &other); + + /* Compare value: bits lost for escaping -- bits lost for prefix */ + c = (escBits+3)*escaped + other*escBits; + if ((flags & F_STATS)) { + fprintf(stderr, " %d:%d", escBits, c); + fflush(stderr); /* for SAS/C */ + } + if (c < mv) { + mb = escBits; + mv = c; + } else { + /* minimum found */ + break; + } + if (escBits==4 && (flags & F_STATS)) + fprintf(stderr, "\n"); + } + if (mb==1) { /* Minimum was 1, check 0 */ + int escaped; + + escBits = 0; + escMask = 0; + + /* Find the optimum path for selected escape bits (no optimize) */ + OptimizeLength(0); + /* Optimize the escape selections for this path & escBits */ + escaped = OptimizeEscape(&escape, NULL); + + if ((flags & F_STATS)) { + fprintf(stderr, " %d:%d", escBits, 3*escaped); + fflush(stderr); /* for SAS/C */ + } + if (3*escaped < mv) { + mb = 0; + /* mv = 3*escaped; */ + } + } + if ((flags & F_STATS)) + fprintf(stderr, "\n"); + + fprintf(stderr, "Selected %d-bit escapes\n", mb); + escBits = mb; + escMask = (0xff00>>escBits) & 0xff; + } + + if (!(flags & F_NOOPT)) { + fprintf(stderr, "Optimizing LZ77 and RLE lengths..."); + fflush(stderr); /* for SAS/C */ + } + + /* Find the optimum path (optimize) */ + OptimizeLength((flags & F_NOOPT)?0:1); + if ((flags & F_STATS)) { + if(!(flags & F_NOOPT)) + fprintf(stderr, " gained %d units.\n", lzopt/8); + } else + fprintf(stderr, "\n"); + + if (1 || (flags & F_AUTOEX)) { + long lzstat[5] = {0,0,0,0,0}, i, cur = 0, old = extraLZPosBits; + + fprintf(stderr, "Selecting LZPOS LO length.. "); + fflush(stderr); /* for SAS/C */ + + for (p=0; p> 8)+1 > (1< (1< (1<<(maxGamma-1))) { + if (rle[p] <= (1< 10) { + fprintf(stderr, + "Note: Using option -m%d you may get better results.\n", + maxGamma+1); + } + if (maxGamma > 5 && stat[0] + stat[1] + stat[3] < 4) { + fprintf(stderr, + "Note: Using option -m%d you may get better results.\n", + maxGamma-1); + } + } + + /* Optimize the escape selections */ + OptimizeEscape(&escape, NULL); + if (startEscape) + *startEscape = escape; + OptimizeRle(flags); /* Retune the RLE selections */ + +#ifdef ENABLE_VERBOSE + if ((flags & F_VERBOSE)) { + int oldEscape = escape; + printf("normal RLE LZLEN LZPOS(absolute)\n\n"); + + for (p=0; p"); + j += lzlen2[p]; + } else + printf(" "); + if (lzpos2) { + printf(" %04x*%03d*+%02x", lzpos2[p], lzlen2[p], + (indata[p] - indata[p-lzpos2[p]]) & 0xff); + } + printf(" 001 %03d %03d %04x(%04x) %02x %s\n", + rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p], + (mode[p] & MMARK)?"#":" "); + break; +#endif + case MMARK | LITERAL: + case LITERAL: + if (j==p) { + printf(">"); + } else + printf(" "); +#ifdef DELTA + if (lzpos2) { + printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p], + (indata[p] - indata[p-lzpos2[p]]) & 0xff); + } +#endif + if (j==p) { + printf("*001* %03d %03d %04x(%04x) %02x %s %02x", + rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p], + (mode[p] & MMARK)?"#":" ", newesc[p]); + if ((indata[p] & escMask) == escape) { + escape = newesc[p]; + printf("«"); + } + printf("\n"); + j += 1; + } else { + printf("*001* %03d %03d %04x(%04x) %02x %s %02x\n", + rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p], + (mode[p] & MMARK)?"#":" ", newesc[p]); + } + break; + case MMARK | LZ77: + case LZ77: + if (j==p) { + printf(">"); + j += lzlen[p]; + } else + printf(" "); +#ifdef DELTA + if (lzpos2) { + printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p], + (indata[p] - indata[p-lzpos2[p]]) & 0xff); + } +#endif + printf(" 001 %03d *%03d* %04x(%04x) %02x %s", + rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p], + (mode[p] & MMARK)?"#":" "); + + printf("\n"); + + break; + case MMARK | RLE: + case RLE: + if (j==p) { + printf(">"); + j += rle[p]; + } else + printf(" "); +#ifdef DELTA + if (lzpos2) { + printf(" %04x %03d +%02x", lzpos2[p], lzlen2[p], + (indata[p] - indata[p-lzpos2[p]]) & 0xff); + } +#endif + printf(" 001 *%03d* %03d %04x(%04x) %02x %s\n", + rle[p], lzlen[p], lzpos[p], p-lzpos[p], indata[p], + (mode[p] & MMARK)?"#":" "); + break; + default: + j++; + break; + } + mode[p] &= ~MMARK; + } + escape = oldEscape; + } +#endif /* ENABLE_VERBOSE */ + + + /* Perform rescan */ + { + int esc = escape; + + for (p=0; p 2 /*&& lzlen[p] > rle[p]*/) { + int bot = p - lzpos[p] + 1, i; + unsigned short rlep = rle[p]; + + if (!rlep) + rlep = 1; + if (bot < 0) + bot = 0; + bot += (rlep-1); + + i = p - (int)backSkip[p]; + while (i>=bot /* && i>=rlep-1 */) { + /* Equal number of A's ? */ + if (rlep==1 || rle[i-rlep+1]==rlep) { /* 'head' matches */ + unsigned char *a = indata + i+1; /* match */ + unsigned char *b = indata + p+rlep-1+1; /* curpos */ + int topindex = inlen-(p+rlep-1); + + j = 1; + while (j < topindex && *a++==*b++) + j++; + + if (j + rlep-1 >= lzlen[p]) { + int tmppos = p-i+rlep-1; + + rescan += + LenLz(lzlen[p], lzpos[p]) - + LenLz(lzlen[p], tmppos); +#if 0 + printf("@%d, lzlen %d, pos %04x -> %04x\n", + p, lzlen[p], lzpos[p], tmppos); + for (i=-1; i<=lzlen[p]; i++) { + printf("%02x %02x %02x ", + indata[p+i], indata[p-lzpos[p]+i], + indata[p-tmppos+i]); + } + printf("\n"); +#endif + lzpos[p] = tmppos; + break; + } + } + if (!backSkip[i]) + break; /* No previous occurrances (near enough) */ + i -= (int)backSkip[i]; + } + } +#endif /* RESCAN */ +#endif /* BACKSKIP_FULL */ + + p += lzlen[p]; + break; + + case RLE: /* rle */ + p += rle[p]; + break; + + default: /* Error Flynn :-) */ + fprintf(stderr, "Internal error: mode %d\n", mode[p]); + p++; + break; + } + } + } + + + /* start of output */ + + for (p=0; p memEnd) { + type |= FIXF_WRAP; + } else { + type &= ~FIXF_WRAP; + } + headerSize = GetHeaderSize(type, NULL) + rleUsed - 15; + } + outlen = outPointer + headerSize; /* unpack code */ + fprintf(stderr, "In: %d, out: %d, ratio: %5.2f%% (%4.2f[%4.2f] b/B)" + ", gained: %5.2f%%\n", + inlen, outlen, (double)outlen*100.0/(double)inlen + 0.005, + 8.0*(double)outlen/(double)inlen + 0.005, + 8.0*(double)(outlen-headerSize+rleUsed+4)/(double)inlen + 0.005, + 100.0 - (double)outlen*100.0/(double)inlen + 0.005); + +#ifdef DELTA + if ((type & FIXF_DLZ)) { + fprintf(stderr, "Gained RLE: %d (S+L:%d+%d), DLZ: %d, LZ: %d, Esc: %d" + ", Decompressor: %d\n", + gainedRle/8, gainedSRle/8, gainedLRle/8, gainedDLz/8, + gainedLz/8, -gainedEscaped/8, -headerSize); + + fprintf(stderr, "Times RLE: %d (%d+%d), DLZ: %d, LZ: %d, Esc: %d (normal: %d)" + ", %d escape bit%s\n", + timesRle, timesSRle, timesLRle, timesDLz, + timesLz, timesEscaped, timesNormal, + escBits, (escBits==1)?"":"s" ); + } else +#endif + { + fprintf(stderr, "Gained RLE: %d (S+L:%d+%d), LZ: %d, Esc: %d" + ", Decompressor: %d\n", + gainedRle/8, gainedSRle/8, gainedLRle/8, + gainedLz/8, -gainedEscaped/8, -headerSize); + + fprintf(stderr, "Times RLE: %d (%d+%d), LZ: %d, Esc: %d (normal: %d)" + ", %d escape bit%s\n", + timesRle, timesSRle, timesLRle, + timesLz, timesEscaped, timesNormal, + escBits, (escBits==1)?"":"s" ); + } + if ((flags & F_STATS)) { + const char *ll[] = {"2", "3-4", "5-8", "9-16", "17-32", "33-64", + "65-128", "129-256"}; + fprintf(stderr, "(Gained by RLE Code: %d, LZPOS LO Bits %d" + ", maxLen: %d, tag bit/prim. %4.2f)\n", + gainedRlecode/8 - rleUsed, + extraLZPosBits + 8, + (2< 7) { + fprintf(stderr, "Max length must be 5..7!\n"); + flags |= F_ERROR; + maxGamma = 7; + } +lrange = LRANGE; +maxlzlen = MAXLZLEN; +maxrlelen = MAXRLELEN; + + InitValueLen(); + break; + case 'e': + escBits = tmpval; + if (escBits < 0 || escBits > 8) { + fprintf(stderr, "Escape bits must be 0..8!\n"); + flags |= F_ERROR; + } else + flags &= ~F_AUTO; + escMask = (0xff00>>escBits) & 0xff; + break; + case 'p': + extraLZPosBits = tmpval; + if (extraLZPosBits < 0 || extraLZPosBits > 4) { + fprintf(stderr, + "Extra LZ-pos bits must be 0..4!\n"); + flags |= F_ERROR; + } else + flags &= ~F_AUTOEX; + break; + case 'l': + startAddr = tmpval; + if (startAddr < 0 || startAddr > 0xffff) { + fprintf(stderr, + "Load address must be 0..0xffff!\n"); + flags |= F_ERROR; + } + break; + case 'c': /* 64 (C64), 20 (VIC20), 16/4 (C16/Plus4) */ + machineType = tmpval; + if (machineType != 64 && machineType != 20 && + machineType != 16 && machineType != 4 && + machineType != 128 && machineType != 0) { + fprintf(stderr, "Machine must be 64, 20, 16/4, 128!\n"); + flags |= F_ERROR; + } + break; + case 'i': /* Interrupt config */ + if (tmpval==0) { + intConfig = 0x78; /* sei */ + } else { + intConfig = 0x58; /* cli */ + } + break; + case 'g': /* Memory configuration */ + memConfig = (tmpval & 0xff); + break; + } + break; + + default: + fprintf(stderr, "Error: Unknown option \"%c\"\n", + argv[n][i]); + flags |= F_ERROR; + } + i++; + } + } else { + if (!fileIn) { + fileIn = argv[n]; + } else if (!fileOut) { + fileOut = argv[n]; + } else { + fprintf(stderr, "Only two filenames wanted!\n"); + flags |= F_ERROR; + } + } + } + + if ((flags & F_ERROR)) { + fprintf(stderr, "Usage: %s [-] [ []]\n", + argv[0]); + fprintf(stderr, + "\t -flist list all decompressors\n" + "\t -ffast select faster version, if available (longer)\n" + "\t -fshort select shorter version, if available (slower)\n" + "\t -fbasic select version for BASIC programs (for VIC20 and C64)\n" +#ifdef DELTA + "\t -fdelta use delta-lz77 -- shortens some files\n" +#endif + "\t -f enable fast mode for C128 (C64 mode) and C16/+4 (default)\n" + "\t +f disable fast mode for C128 (C64 mode) and C16/+4\n" + "\t c machine: 64 (C64), 20 (VIC20), 16 (C16/+4)\n" + "\t a avoid video matrix (for VIC20)\n" + "\t d data (no loading address)\n" + "\t l set/override load address\n" + "\t x set execution address\n" + "\t e force escape bits\n" + "\t r restrict lz search range\n" + "\t n no RLE/LZ length optimization\n" + "\t s full statistics\n" +#ifdef ENABLE_VERBOSE + "\t v verbose\n" +#endif /* ENABLE_VERBOSE */ + "\t p force extralzposbits\n" + "\t m max len 5..7 (2*2^5..2*2^7)\n" + "\t i interrupt enable after decompress (0=disable)\n" + "\t g memory configuration after decompress\n" + "\t u unpack\n"); + return EXIT_FAILURE; + } + + if (lzlen == -1) + lzlen = DEFAULT_LZLEN; + + if (fileIn) { + if (!(infp = fopen(fileIn, "rb"))) { + fprintf(stderr, "Could not open %s for reading!\n", fileIn); + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Reading from stdin\n"); + fflush(stderr); /* for SAS/C */ + infp = stdin; + } + + if (!(flags & F_SKIP)) { + ssize_t bytes_read = fread(tmp, 1, 2, infp); + (void) bytes_read; + /* Use it only if not overriden by the user */ + if (startAddr==-1) + startAddr = tmp[0] + 256*tmp[1]; + } + if (startAddr==-1) + startAddr = 0x258; + + /* Read in the data */ + inlen = 0; + buflen = 0; + indata = NULL; + while (1) { + if (buflen < inlen + lrange) { + unsigned char *tmp = realloc(indata, buflen + lrange); + if (!tmp) { + free(indata); + return 20; + } + indata = tmp; + buflen += lrange; + } + newlen = fread(indata + inlen, 1, lrange, infp); + if (newlen <= 0) + break; + inlen += newlen; + } + if (infp != stdin) + fclose(infp); + + if ((flags & F_UNPACK)) { + n = UnPack(startAddr, indata, fileOut, flags); + if (indata) + free(indata); + return n; + } + + if (startAddr < 0x258 +#ifndef BIG + || startAddr + inlen -1 > 0xffff +#endif /* BIG */ + ) { + fprintf(stderr, + "Only programs from 0x0258 to 0xffff can be compressed\n"); + fprintf(stderr, "(the input file is from 0x%04x to 0x%04x)\n", + startAddr, startAddr+inlen-1); + if (indata) + free(indata); + return EXIT_FAILURE; + } + + switch (machineType) { + case 20: + machineTypeTxt = "VIC20 with 8k or 16k (or 24k) expansion memory"; + memStart = 0x1201; + memEnd = 0x4000; + type |= FIXF_VIC20 | FIXF_WRAP; + + if (startAddr+inlen > 0x8000) { + fprintf(stderr, "Original file exceeds 0x8000 (0x%04x), " + "not a valid VIC20 file!\n", startAddr+inlen-1); + n = EXIT_FAILURE; + goto errexit; + } else if (startAddr+inlen > 0x6000) { + if (startAddr < 0x1000) { + fprintf(stderr, "Original file exceeds 0x6000 (0x%04x), " + "3kB+24kB memory expansions assumed\n", + startAddr+inlen-1); + machineTypeTxt = "VIC20 with 3k+24k expansion memory"; + } else { + fprintf(stderr, "Original file exceeds 0x6000 (0x%04x), " + "24kB memory expansion assumed\n", + startAddr+inlen-1); + machineTypeTxt = "VIC20 with 24k expansion memory"; + } + memEnd = 0x8000; + } else if (startAddr+inlen > 0x4000) { + if (startAddr < 0x1000) { + fprintf(stderr, "Original file exceeds 0x4000 (0x%04x), " + "3kB+16kB memory expansion assumed\n", + startAddr+inlen-1); + machineTypeTxt = + "VIC20 with 3k+16k (or 3k+24k) expansion memory"; + } else { + fprintf(stderr, "Original file exceeds 0x4000 (0x%04x), " + "16kB memory expansion assumed\n", + startAddr+inlen-1); + machineTypeTxt = "VIC20 with 16k (or 24k) expansion memory"; + } + memEnd = 0x6000; + } else if (startAddr+inlen > 0x2000) { + if (startAddr < 0x1000) { + fprintf(stderr, "Original file exceeds 0x2000 (0x%04x), " + "3kB+8kB memory expansion assumed\n", + startAddr+inlen-1); + machineTypeTxt = + "VIC20 with 3k+8k (or 3k+16k, or 3k+24k) expansion memory"; + } else { + fprintf(stderr, "Original file exceeds 0x2000 (0x%04x), " + "8kB memory expansion assumed\n", + startAddr+inlen-1); + } + /* memEnd = 0x4000; */ + } else { + if (startAddr >= 0x1000 && startAddr < 0x1200) { + fprintf(stderr, "Program for unexpanded VIC detected.\n"); + memStart = 0x1001; + memEnd = (flags & F_AVOID)?0x1e00:0x2000; + machineTypeTxt = "VIC20 without expansion memory"; + } if (startAddr >= 0x400 && startAddr < 0x1000) { + fprintf(stderr, "Program for 3k-expanded VIC detected.\n"); + memStart = 0x0401; + memEnd = (flags & F_AVOID)?0x1e00:0x2000; + machineTypeTxt = "VIC20 with 3k expansion memory"; + } + } + break; + case 16: + case 4: + type |= FIXF_C16 | FIXF_WRAP; + if (startAddr+inlen > 0x4000) { + fprintf(stderr, "Original file exceeds 0x4000, 61k RAM assumed\n"); + memStart = 0x1001; + memEnd = 0xfd00; + machineTypeTxt = "Plus/4"; + } else { + fprintf(stderr, "Program for unexpanded C16 detected.\n"); + memStart = 0x1001; + memEnd = 0x4000; + machineTypeTxt = "Commodore 16"; + } + break; + case 128: + type |= FIXF_C128 | FIXF_WRAP; + memStart = 0x1c01; + memEnd = 0x10000; + machineTypeTxt = "Commodore 128"; + break; + case 0: + type |= 0; + machineTypeTxt = "Without decompressor"; + memStart = 0x801; + memEnd = 0x10000; + break; + default: /* C64 */ + type |= FIXF_C64 | FIXF_WRAP; /* C64, wrap active */ + machineTypeTxt = "Commodore 64"; + memStart = 0x801; /* Loading address */ + memEnd = 0x10000; + break; + } + + if (startAddr <= memStart) { + for (n=memStart-startAddr; n='0' && indata[n]<='9') { + execAddr = execAddr * 10 + indata[n++] - '0'; + } + break; + } + } + } + if (ea != -1) { + if (execAddr!=-1 && ea!=execAddr) + fprintf(stderr, "Discarding execution address 0x%04x=%d\n", + execAddr, execAddr); + execAddr = ea; + } else if (execAddr < startAddr || execAddr >= startAddr+inlen) { + if ((type & FIXF_BASIC)) { + execAddr = 0xa7ae; + } else { + fprintf(stderr, "Note: The execution address was not detected " + "correctly!\n"); + fprintf(stderr, " Use the -x option to set the execution " + "address.\n"); + } + } + fprintf(stderr, "Load address 0x%04x=%d, Last byte 0x%04x=%d\n", + startAddr, startAddr, startAddr+inlen-1, startAddr+inlen-1); + fprintf(stderr, "Exec address 0x%04x=%d\n", execAddr, execAddr); + fprintf(stderr, "New load address 0x%04x=%d\n", memStart, memStart); + if (machineType == 64) { + fprintf(stderr, "Interrupts %s and memory config set to $%02x " + "after decompression\n", + (intConfig==0x58)?"enabled":"disabled", memConfig); + fprintf(stderr, "Runnable on %s\n", machineTypeTxt); + } else if (machineType != 0) { + fprintf(stderr, "Interrupts %s after decompression\n", + (intConfig==0x58)?"enabled":"disabled"); + fprintf(stderr, "Runnable on %s\n", machineTypeTxt); + } else { + fprintf(stderr, "Standalone decompressor required\n"); + } +#if 0 + if ((flags & F_2MHZ)) + type |= FIXF_FAST; +#endif + n = PackLz77(lzlen, flags, &startEscape, startAddr + inlen, memEnd, type); + if (!n) { + int endAddr = startAddr + inlen; /* end for uncompressed data */ + int hDeCall, progEnd = endAddr; + + if (GetHeaderSize(type, &hDeCall) == 0) { + GetHeaderSize(type & ~FIXF_WRAP, &hDeCall); + } + if (machineType != 0 && + endAddr - ((outPointer + 255) & ~255) < memStart + hDeCall + 3) { + /* would overwrite the decompressor, move a bit upwards */ + fprintf(stderr, "$%x < $%x, decompressor overwrite possible, " + "moving upwards\n", + endAddr - ((outPointer + 255) & ~255), + memStart + hDeCall + 3); + endAddr = memStart + hDeCall + 3 + ((outPointer + 255) & ~255); + } + /* Should check that endAddr really is larger than original endaddr! */ +#if 0 + /* Move the end address for files that got expanded */ + if (memStart + hSize + outPointer > endAddr) { + endAddr = memStart + hSize + outPointer; + } +#endif + /* 3 bytes reserved for EOF */ + /* bytes reserved for temporary data expansion (escaped chars) */ + endAddr += 3 + reservedBytes; + +#ifdef BIG + endAddr = 0x10000; +#endif /* BIG */ +#ifdef DELTA + if (!timesDLz) { + type &= ~FIXF_DLZ; + } +#endif + SavePack(type, outBuffer, outPointer, fileOut, + startAddr, execAddr, startEscape, rleValues, + endAddr, progEnd, extraLZPosBits, (flags & F_2MHZ)?1:0, + memStart, memEnd); + + timeused = clock()-timeused; + if (!timeused) + timeused++; + fprintf(stderr, + "Compressed %d bytes in %4.2f seconds (%4.2f kB/sec)\n", + inlen, + (double)timeused/CLOCKS_PER_SEC, + (double)CLOCKS_PER_SEC*inlen/timeused/1024.0); + } +errexit: + if (indata) + free(indata); + return n; +} + + diff --git a/loader/tools/pucrunch/pucrunch.h b/loader/tools/pucrunch/pucrunch.h new file mode 100644 index 0000000..635c287 --- /dev/null +++ b/loader/tools/pucrunch/pucrunch.h @@ -0,0 +1,2189 @@ +enum FixType { + ftFastDisable, /* -> 0x2c; bit $nnnn */ + + ftOverlap, /* overlap?(overlap-1):0 */ + ftOverlapLo, /* (0x801 + (sizeof(headerUncrunch)-2+rleUsed-31) + + size - overlap) & 0xff; */ + ftOverlapHi, /* (0x801 + (sizeof(headerUncrunch)-2+rleUsed-31) + + size - overlap) >> 8; */ + ftWrapCount, /* (memend>>8) - ((endAddr + overlap - size) >> 8); */ + ftSizePages, /* (size>>8) + 1 */ + ftSizeLo, /* (0x801 + (sizeof(headerUncrunchNoWrap)-2+rleUsed-31) + + size - 0x100) & 0xff; */ + ftSizeHi, /* (0x801+ (sizeof(headerUncrunchNoWrap)-2+rleUsed-31) + + size - 0x100) >> 8; */ + ftEndLo, /* (endAddr - 0x100) & 0xff; */ + ftEndHi, /* ((endAddr - 0x100) >> 8); */ + ftEscValue, /* (escape>>(8-escBits)); */ + ftOutposLo, /* (start & 0xff); */ + ftOutposHi, /* (start >> 8); */ + ftEscBits, /* escBits; */ + ftEsc8Bits, /* 8-escBits; */ + ft1MaxGamma, /* (1<> 8); */ + ftInposLo, /* (endAddr - size) & 0xff; */ + ftInposHi, /* ((endAddr - size) >> 8); */ + ftMaxGamma, /* maxGamma + 1; */ + + ftBEndLo, /* basic end address */ + ftBEndHi, + + ftIBufferSize, /* # of bytes in system input buffer $200- */ + ftStackSize, /* # of bytes in zero page and stack $0f7- */ + ftDeCall, + + ftOp, /* op for ADC-lz */ + + ftReloc, + ftEnd +}; + +struct FixEntry { + enum FixType type; + int offset; +}; + +#define FIXF_C64 1 +#define FIXF_VIC20 2 +#define FIXF_C16 4 +#define FIXF_C128 8 +#define FIXF_MACHMASK 0xff /* Must be exactly correct */ + +#define FIXF_WRAP 256 /* If requested, must be present */ +#define FIXF_DLZ 512 /* If requested, must be present */ +#define FIXF_BASIC 1024 /* If requested, must be present */ + +#define FIXF_FAST 2048 +#define FIXF_SHORT 4096 + +#define FIXF_MUSTMASK (FIXF_WRAP|FIXF_DLZ|FIXF_BASIC) + + +struct FixStruct { + unsigned char *code; + int codeSize; + struct FixEntry *fixes; + const char *name; + int flags; +}; + + +static unsigned char headerC64SW[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x38,0x85,0x01,0xa9,0xaa,0x85,0x2d,0xa9, + 0xaa,0x85,0x2e,0xa2,0x00,0xbd,0xaa,0xaa, + 0x95,0x4b,0xca,0x10,0xf8,0xa2,0xe7,0xbd, + 0x46,0x08,0x9d,0xf6,0x00,0xca,0xd0,0xf7, + 0xa0,0xaa,0xca,0xbd,0xaa,0xaa,0x9d,0x00, + 0xff,0x8a,0xd0,0xf6,0xce,0x37,0x08,0xce, + 0x34,0x08,0x88,0xd0,0xed,0x4c,0x16,0x01, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0, + 0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2, + 0x02,0x20,0xca,0x01,0x85,0xf8,0x98,0xa2, + 0x06,0x20,0xca,0x01,0x20,0xf9,0x00,0xa0, + 0x00,0x98,0xa2,0x02,0x20,0xca,0x01,0xc5, + 0xf8,0xd0,0xec,0x20,0xb7,0x01,0x85,0x2f, + 0x4a,0xd0,0x3d,0x20,0x9b,0x01,0x90,0x46, + 0x20,0x9b,0x01,0x90,0xd0,0xc8,0x20,0xb7, + 0x01,0x85,0x2f,0xc9,0x40,0x90,0x0b,0xa2, + 0x02,0x20,0xc5,0x01,0x85,0x2f,0x20,0xb7, + 0x01,0xa8,0x20,0xb7,0x01,0xaa,0xbd,0xcd, + 0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2,0x04, + 0x20,0xc5,0x01,0xa6,0x2f,0xe8,0x20,0xf9, + 0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0,0xaf, + 0x20,0xb7,0x01,0xc9,0x7f,0xf0,0x25,0xe9, + 0x00,0xa2,0x00,0x20,0xca,0x01,0x85,0x30, + 0xa2,0x08,0x20,0xc5,0x01,0x65,0xfa,0xa6, + 0x2f,0x85,0x2f,0xa5,0xfb,0xe5,0x30,0x85, + 0x30,0xe8,0xb1,0x2f,0xc8,0x20,0xf9,0x00, + 0xd0,0xf8,0xf0,0xd2,0xa9,0x37,0x85,0x01, + 0x58,0x4c,0xaa,0xaa,0x06,0xf7,0xd0,0x17, + 0x48,0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee, + 0xa1,0x01,0xd0,0x0a,0xee,0xa2,0x01,0xd0, + 0x05,0xa9,0x4b,0x8d,0xa1,0x01,0x68,0x60, + 0xe8,0x8a,0x20,0x9b,0x01,0x90,0x0b,0xe8, + 0xe0,0x07,0xd0,0xf6,0xf0,0x04,0x20,0x9b, + 0x01,0x2a,0xca,0xd0,0xf9,0x18,0x60,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64SW[] = { + {ftBEndHi, 0x0817 -0x07ff}, + {ftOverlap, 0x081b -0x07ff}, + {ftOverlapLo, 0x081d -0x07ff}, + {ftOverlapHi, 0x081e -0x07ff}, + {ftStackSize, 0x0825 -0x07ff}, + {ftReloc, 0x0828 -0x07ff}, + {ftSizePages, 0x0830 -0x07ff}, + {ftSizeLo, 0x0833 -0x07ff}, + {ftSizeHi, 0x0834 -0x07ff}, + {ftEndLo, 0x0836 -0x07ff}, + {ftEndHi, 0x0837 -0x07ff}, + {ftReloc, 0x083d -0x07ff}, + {ftReloc, 0x0840 -0x07ff}, + {ftDeCall, 0x0844 -0x07ff}, + {ftEscValue, 0x0848 -0x07ff}, + {ftOutposLo, 0x084a -0x07ff}, + {ftOutposHi, 0x084b -0x07ff}, + {ftEscBits, 0x0857 -0x07ff}, + {ftEsc8Bits, 0x085f -0x07ff}, + {ftEscBits, 0x086a -0x07ff}, + {ft1MaxGamma, 0x088b -0x07ff}, + {ft8MaxGamma, 0x088f -0x07ff}, + {ft2MaxGamma, 0x08bb -0x07ff}, + {ftExtraBits, 0x08c1 -0x07ff}, + {ftMemConfig, 0x08e4 -0x07ff}, + {ftCli, 0x08e7 -0x07ff}, + {ftExecLo, 0x08e9 -0x07ff}, + {ftExecHi, 0x08ea -0x07ff}, + {ftInposLo, 0x08f1 -0x07ff}, + {ftInposHi, 0x08f2 -0x07ff}, + {ftMaxGamma, 0x0910 -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64S[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x38,0x85,0x01,0xa9,0xaa,0x85,0x2d,0xa9, + 0xaa,0x85,0x2e,0xa2,0xe0,0xbd,0x3c,0x08, + 0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa, + 0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a, + 0xd0,0xf6,0xce,0x2d,0x08,0xce,0x2a,0x08, + 0x88,0xd0,0xed,0x4c,0x16,0x01,0x80,0x00, + 0x8d,0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6, + 0xfb,0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20, + 0xc3,0x01,0x85,0xf8,0x98,0xa2,0x06,0x20, + 0xc3,0x01,0x20,0xf9,0x00,0xa0,0x00,0x98, + 0xa2,0x02,0x20,0xc3,0x01,0xc5,0xf8,0xd0, + 0xec,0x20,0xb0,0x01,0x85,0x2f,0x4a,0xd0, + 0x3d,0x20,0x9b,0x01,0x90,0x46,0x20,0x9b, + 0x01,0x90,0xd0,0xc8,0x20,0xb0,0x01,0x85, + 0x2f,0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20, + 0xbe,0x01,0x85,0x2f,0x20,0xb0,0x01,0xa8, + 0x20,0xb0,0x01,0xaa,0xbd,0xc6,0x01,0xe0, + 0x10,0x90,0x06,0x8a,0xa2,0x04,0x20,0xbe, + 0x01,0xa6,0x2f,0xe8,0x20,0xf9,0x00,0xd0, + 0xfb,0x88,0xd0,0xf8,0xf0,0xaf,0x20,0xb0, + 0x01,0xc9,0x7f,0xf0,0x25,0xe9,0x00,0xa2, + 0x00,0x20,0xc3,0x01,0x85,0x30,0xa2,0x08, + 0x20,0xbe,0x01,0x65,0xfa,0xa6,0x2f,0x85, + 0x2f,0xa5,0xfb,0xe5,0x30,0x85,0x30,0xe8, + 0xb1,0x2f,0xc8,0x20,0xf9,0x00,0xd0,0xf8, + 0xf0,0xd2,0xa9,0x37,0x85,0x01,0x58,0x4c, + 0xaa,0xaa,0x06,0xf7,0xd0,0x10,0x48,0xad, + 0xaa,0xaa,0x2a,0x85,0xf7,0xee,0xa1,0x01, + 0xd0,0x03,0xee,0xa2,0x01,0x68,0x60,0xe8, + 0x8a,0x20,0x9b,0x01,0x90,0x0b,0xe8,0xe0, + 0x07,0xd0,0xf6,0xf0,0x04,0x20,0x9b,0x01, + 0x2a,0xca,0xd0,0xf9,0x18,0x60,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64S[] = { + {ftBEndHi, 0x0817 -0x07ff}, + {ftStackSize, 0x081b -0x07ff}, + {ftReloc, 0x081e -0x07ff}, + {ftSizePages, 0x0826 -0x07ff}, + {ftSizeLo, 0x0829 -0x07ff}, + {ftSizeHi, 0x082a -0x07ff}, + {ftEndLo, 0x082c -0x07ff}, + {ftEndHi, 0x082d -0x07ff}, + {ftReloc, 0x0833 -0x07ff}, + {ftReloc, 0x0836 -0x07ff}, + {ftDeCall, 0x083a -0x07ff}, + {ftEscValue, 0x083e -0x07ff}, + {ftOutposLo, 0x0840 -0x07ff}, + {ftOutposHi, 0x0841 -0x07ff}, + {ftEscBits, 0x084d -0x07ff}, + {ftEsc8Bits, 0x0855 -0x07ff}, + {ftEscBits, 0x0860 -0x07ff}, + {ft1MaxGamma, 0x0881 -0x07ff}, + {ft8MaxGamma, 0x0885 -0x07ff}, + {ft2MaxGamma, 0x08b1 -0x07ff}, + {ftExtraBits, 0x08b7 -0x07ff}, + {ftMemConfig, 0x08da -0x07ff}, + {ftCli, 0x08dd -0x07ff}, + {ftExecLo, 0x08df -0x07ff}, + {ftExecHi, 0x08e0 -0x07ff}, + {ftInposLo, 0x08e7 -0x07ff}, + {ftInposHi, 0x08e8 -0x07ff}, + {ftMaxGamma, 0x08ff -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64SB[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x38,0x85,0x01,0xa9,0xaa,0x85,0x2d,0xa9, + 0xaa,0x85,0x2e,0xa2,0xe5,0xbd,0x3c,0x08, + 0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa, + 0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a, + 0xd0,0xf6,0xce,0x2d,0x08,0xce,0x2a,0x08, + 0x88,0xd0,0xed,0x4c,0x16,0x01,0x80,0x00, + 0x8d,0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6, + 0xfb,0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20, + 0xc8,0x01,0x85,0xf8,0x98,0xa2,0x06,0x20, + 0xc8,0x01,0x20,0xf9,0x00,0xa0,0x00,0x98, + 0xa2,0x02,0x20,0xc8,0x01,0xc5,0xf8,0xd0, + 0xec,0x20,0xb5,0x01,0x85,0x2f,0x4a,0xd0, + 0x3d,0x20,0xa0,0x01,0x90,0x46,0x20,0xa0, + 0x01,0x90,0xd0,0xc8,0x20,0xb5,0x01,0x85, + 0x2f,0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20, + 0xc3,0x01,0x85,0x2f,0x20,0xb5,0x01,0xa8, + 0x20,0xb5,0x01,0xaa,0xbd,0xcb,0x01,0xe0, + 0x10,0x90,0x06,0x8a,0xa2,0x04,0x20,0xc3, + 0x01,0xa6,0x2f,0xe8,0x20,0xf9,0x00,0xd0, + 0xfb,0x88,0xd0,0xf8,0xf0,0xaf,0x20,0xb5, + 0x01,0xc9,0x7f,0xf0,0x25,0xe9,0x00,0xa2, + 0x00,0x20,0xc8,0x01,0x85,0x30,0xa2,0x08, + 0x20,0xc3,0x01,0x65,0xfa,0xa6,0x2f,0x85, + 0x2f,0xa5,0xfb,0xe5,0x30,0x85,0x30,0xe8, + 0xb1,0x2f,0xc8,0x20,0xf9,0x00,0xd0,0xf8, + 0xf0,0xd2,0xa9,0x37,0x85,0x01,0x58,0xa9, + 0x00,0x20,0x71,0xa8,0x4c,0xae,0xa7,0x06, + 0xf7,0xd0,0x10,0x48,0xad,0xaa,0xaa,0x2a, + 0x85,0xf7,0xee,0xa6,0x01,0xd0,0x03,0xee, + 0xa7,0x01,0x68,0x60,0xe8,0x8a,0x20,0xa0, + 0x01,0x90,0x0b,0xe8,0xe0,0x07,0xd0,0xf6, + 0xf0,0x04,0x20,0xa0,0x01,0x2a,0xca,0xd0, + 0xf9,0x18,0x60,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 +}; + +struct FixEntry fixTableC64SB[] = { + {ftBEndHi, 0x0817 -0x07ff}, + {ftStackSize, 0x081b -0x07ff}, + {ftReloc, 0x081e -0x07ff}, + {ftSizePages, 0x0826 -0x07ff}, + {ftSizeLo, 0x0829 -0x07ff}, + {ftSizeHi, 0x082a -0x07ff}, + {ftEndLo, 0x082c -0x07ff}, + {ftEndHi, 0x082d -0x07ff}, + {ftReloc, 0x0833 -0x07ff}, + {ftReloc, 0x0836 -0x07ff}, + {ftDeCall, 0x083a -0x07ff}, + {ftEscValue, 0x083e -0x07ff}, + {ftOutposLo, 0x0840 -0x07ff}, + {ftOutposHi, 0x0841 -0x07ff}, + {ftEscBits, 0x084d -0x07ff}, + {ftEsc8Bits, 0x0855 -0x07ff}, + {ftEscBits, 0x0860 -0x07ff}, + {ft1MaxGamma, 0x0881 -0x07ff}, + {ft8MaxGamma, 0x0885 -0x07ff}, + {ft2MaxGamma, 0x08b1 -0x07ff}, + {ftExtraBits, 0x08b7 -0x07ff}, + {ftMemConfig, 0x08da -0x07ff}, + {ftCli, 0x08dd -0x07ff}, + {ftInposLo, 0x08ec -0x07ff}, + {ftInposHi, 0x08ed -0x07ff}, + {ftMaxGamma, 0x0904 -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64W[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x00, + 0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10,0xf8, + 0xa2,0x3b,0xbd,0x4c,0x08,0x9d,0xff,0x01, + 0xca,0xd0,0xf7,0xa2,0xbf,0xbd,0x86,0x08, + 0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa, + 0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a, + 0xd0,0xf6,0xce,0x3d,0x08,0xce,0x3a,0x08, + 0x88,0xd0,0xed,0x4c,0x16,0x01,0x48,0xad, + 0xaa,0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02, + 0xd0,0x0a,0xee,0x03,0x02,0xd0,0x05,0xa9, + 0x4b,0x8d,0x02,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0, + 0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2, + 0x02,0x20,0x36,0x02,0x85,0xf8,0x98,0xa2, + 0x06,0x20,0x36,0x02,0x20,0xf9,0x00,0xa0, + 0x00,0x98,0xa2,0x02,0x20,0x36,0x02,0xc5, + 0xf8,0xd0,0xec,0x20,0x18,0x02,0x85,0x2d, + 0x4a,0xd0,0x3f,0x20,0x2c,0x02,0x4a,0x90, + 0x47,0x20,0x2c,0x02,0x4a,0x90,0xce,0xc8, + 0x20,0x18,0x02,0x85,0x2d,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x2d,0x02,0x85,0x2d, + 0x20,0x18,0x02,0xa8,0x20,0x18,0x02,0xaa, + 0xbd,0xa5,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x2d,0x02,0xa6,0x2d,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xad,0x20,0x18,0x02,0xc9,0x7f,0xf0, + 0x23,0xe9,0x00,0xa2,0x00,0x20,0x36,0x02, + 0x85,0x2e,0x20,0x2a,0x02,0x65,0xfa,0xa6, + 0x2d,0x85,0x2d,0xa5,0xfb,0xe5,0x2e,0x85, + 0x2e,0xe8,0xb1,0x2d,0xc8,0x20,0xf9,0x00, + 0xd0,0xf8,0xf0,0xd4,0xa9,0x37,0x85,0x01, + 0xce,0x30,0xd0,0xa5,0xfa,0x85,0x2d,0xa5, + 0xfb,0x85,0x2e,0x58,0x4c,0xaa,0xaa,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64W[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftOverlap, 0x0816 -0x07ff}, + {ftOverlapLo, 0x0818 -0x07ff}, + {ftOverlapHi, 0x0819 -0x07ff}, + {ftIBufferSize, 0x0820 -0x07ff}, + {ftReloc, 0x0823 -0x07ff}, + {ftStackSize, 0x082b -0x07ff}, + {ftReloc, 0x082e -0x07ff}, + {ftSizePages, 0x0836 -0x07ff}, + {ftSizeLo, 0x0839 -0x07ff}, + {ftSizeHi, 0x083a -0x07ff}, + {ftEndLo, 0x083c -0x07ff}, + {ftEndHi, 0x083d -0x07ff}, + {ftReloc, 0x0843 -0x07ff}, + {ftReloc, 0x0846 -0x07ff}, + {ftDeCall, 0x084a -0x07ff}, + {ftInposLo, 0x084f -0x07ff}, + {ftInposHi, 0x0850 -0x07ff}, + {ftMaxGamma, 0x0872 -0x07ff}, + {ftEscValue, 0x0888 -0x07ff}, + {ftOutposLo, 0x088a -0x07ff}, + {ftOutposHi, 0x088b -0x07ff}, + {ftEscBits, 0x0897 -0x07ff}, + {ftEsc8Bits, 0x089f -0x07ff}, + {ftEscBits, 0x08aa -0x07ff}, + {ft1MaxGamma, 0x08cd -0x07ff}, + {ft8MaxGamma, 0x08d1 -0x07ff}, + {ft2MaxGamma, 0x08fd -0x07ff}, + {ftExtraBits, 0x0903 -0x07ff}, + {ftMemConfig, 0x0924 -0x07ff}, + {ftFastDisable, 0x0927 -0x07ff}, + {ftCli, 0x0932 -0x07ff}, + {ftExecLo, 0x0934 -0x07ff}, + {ftExecHi, 0x0935 -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x34, + 0xbd,0x42,0x08,0x9d,0xff,0x01,0xca,0xd0, + 0xf7,0xa2,0xbf,0xbd,0x75,0x08,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x33,0x08,0xce,0x30,0x08,0x88,0xd0, + 0xed,0x4c,0x16,0x01,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0,0x03, + 0xee,0x03,0x02,0x68,0x60,0xe8,0x8a,0x06, + 0xf7,0xd0,0x03,0x20,0x00,0x02,0x90,0x12, + 0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b,0xa2, + 0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20,0x00, + 0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60,0x80, + 0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0,0x02, + 0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2,0x02, + 0x20,0x2f,0x02,0x85,0xf8,0x98,0xa2,0x06, + 0x20,0x2f,0x02,0x20,0xf9,0x00,0xa0,0x00, + 0x98,0xa2,0x02,0x20,0x2f,0x02,0xc5,0xf8, + 0xd0,0xec,0x20,0x11,0x02,0x85,0x2d,0x4a, + 0xd0,0x3f,0x20,0x25,0x02,0x4a,0x90,0x47, + 0x20,0x25,0x02,0x4a,0x90,0xce,0xc8,0x20, + 0x11,0x02,0x85,0x2d,0xc9,0x40,0x90,0x0b, + 0xa2,0x02,0x20,0x26,0x02,0x85,0x2d,0x20, + 0x11,0x02,0xa8,0x20,0x11,0x02,0xaa,0xbd, + 0xa5,0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2, + 0x04,0x20,0x26,0x02,0xa6,0x2d,0xe8,0x20, + 0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0, + 0xad,0x20,0x11,0x02,0xc9,0x7f,0xf0,0x23, + 0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02,0x85, + 0x2e,0x20,0x23,0x02,0x65,0xfa,0xa6,0x2d, + 0x85,0x2d,0xa5,0xfb,0xe5,0x2e,0x85,0x2e, + 0xe8,0xb1,0x2d,0xc8,0x20,0xf9,0x00,0xd0, + 0xf8,0xf0,0xd4,0xa9,0x37,0x85,0x01,0xce, + 0x30,0xd0,0xa5,0xfa,0x85,0x2d,0xa5,0xfb, + 0x85,0x2e,0x58,0x4c,0xaa,0xaa,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftIBufferSize, 0x0816 -0x07ff}, + {ftReloc, 0x0819 -0x07ff}, + {ftStackSize, 0x0821 -0x07ff}, + {ftReloc, 0x0824 -0x07ff}, + {ftSizePages, 0x082c -0x07ff}, + {ftSizeLo, 0x082f -0x07ff}, + {ftSizeHi, 0x0830 -0x07ff}, + {ftEndLo, 0x0832 -0x07ff}, + {ftEndHi, 0x0833 -0x07ff}, + {ftReloc, 0x0839 -0x07ff}, + {ftReloc, 0x083c -0x07ff}, + {ftDeCall, 0x0840 -0x07ff}, + {ftInposLo, 0x0845 -0x07ff}, + {ftInposHi, 0x0846 -0x07ff}, + {ftMaxGamma, 0x0861 -0x07ff}, + {ftEscValue, 0x0877 -0x07ff}, + {ftOutposLo, 0x0879 -0x07ff}, + {ftOutposHi, 0x087a -0x07ff}, + {ftEscBits, 0x0886 -0x07ff}, + {ftEsc8Bits, 0x088e -0x07ff}, + {ftEscBits, 0x0899 -0x07ff}, + {ft1MaxGamma, 0x08bc -0x07ff}, + {ft8MaxGamma, 0x08c0 -0x07ff}, + {ft2MaxGamma, 0x08ec -0x07ff}, + {ftExtraBits, 0x08f2 -0x07ff}, + {ftMemConfig, 0x0913 -0x07ff}, + {ftFastDisable, 0x0916 -0x07ff}, + {ftCli, 0x0921 -0x07ff}, + {ftExecLo, 0x0923 -0x07ff}, + {ftExecHi, 0x0924 -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64WD[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x00, + 0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10,0xf8, + 0xa2,0x3b,0xbd,0x4c,0x08,0x9d,0xff,0x01, + 0xca,0xd0,0xf7,0xa2,0xd4,0xbd,0x86,0x08, + 0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa, + 0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a, + 0xd0,0xf6,0xce,0x3d,0x08,0xce,0x3a,0x08, + 0x88,0xd0,0xed,0x4c,0x16,0x01,0x48,0xad, + 0xaa,0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02, + 0xd0,0x0a,0xee,0x03,0x02,0xd0,0x05,0xa9, + 0x4b,0x8d,0x02,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0, + 0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2, + 0x02,0x20,0x36,0x02,0x85,0xf8,0x98,0xa2, + 0x06,0x20,0x36,0x02,0x20,0xf9,0x00,0xa0, + 0x00,0x8c,0x90,0x01,0x98,0xa2,0x02,0x20, + 0x36,0x02,0xc5,0xf8,0xd0,0xe9,0x20,0x18, + 0x02,0x85,0x2d,0x4a,0xd0,0x3f,0x20,0x2c, + 0x02,0x4a,0x90,0x47,0x20,0x2c,0x02,0x4a, + 0x90,0xcb,0xc8,0x20,0x18,0x02,0x85,0x2d, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0x2d, + 0x02,0x85,0x2d,0x20,0x18,0x02,0xa8,0x20, + 0x18,0x02,0xaa,0xbd,0xba,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0x2d,0x02, + 0xa6,0x2d,0xe8,0x20,0xf9,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xaa,0x20,0x18,0x02, + 0xc9,0x7f,0xf0,0x26,0xe9,0x00,0xa2,0x00, + 0x20,0x36,0x02,0x85,0x2e,0x20,0x2a,0x02, + 0x65,0xfa,0xa6,0x2d,0x85,0x2d,0xa5,0xfb, + 0xe5,0x2e,0x85,0x2e,0xe8,0xb1,0x2d,0x18, + 0x69,0x00,0xc8,0x20,0xf9,0x00,0xd0,0xf5, + 0xf0,0xd1,0xa5,0x2d,0xc9,0x02,0xf0,0x09, + 0x20,0x2a,0x02,0x8d,0x90,0x01,0x98,0xf0, + 0xd2,0xa9,0x37,0x85,0x01,0xce,0x30,0xd0, + 0xa5,0xfa,0x85,0x2d,0xa5,0xfb,0x85,0x2e, + 0x58,0x4c,0xaa,0xaa,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64WD[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftOverlap, 0x0816 -0x07ff}, + {ftOverlapLo, 0x0818 -0x07ff}, + {ftOverlapHi, 0x0819 -0x07ff}, + {ftIBufferSize, 0x0820 -0x07ff}, + {ftReloc, 0x0823 -0x07ff}, + {ftStackSize, 0x082b -0x07ff}, + {ftReloc, 0x082e -0x07ff}, + {ftSizePages, 0x0836 -0x07ff}, + {ftSizeLo, 0x0839 -0x07ff}, + {ftSizeHi, 0x083a -0x07ff}, + {ftEndLo, 0x083c -0x07ff}, + {ftEndHi, 0x083d -0x07ff}, + {ftReloc, 0x0843 -0x07ff}, + {ftReloc, 0x0846 -0x07ff}, + {ftDeCall, 0x084a -0x07ff}, + {ftInposLo, 0x084f -0x07ff}, + {ftInposHi, 0x0850 -0x07ff}, + {ftMaxGamma, 0x0872 -0x07ff}, + {ftEscValue, 0x0888 -0x07ff}, + {ftOutposLo, 0x088a -0x07ff}, + {ftOutposHi, 0x088b -0x07ff}, + {ftEscBits, 0x0897 -0x07ff}, + {ftEsc8Bits, 0x089f -0x07ff}, + {ftEscBits, 0x08ad -0x07ff}, + {ft1MaxGamma, 0x08d0 -0x07ff}, + {ft8MaxGamma, 0x08d4 -0x07ff}, + {ft2MaxGamma, 0x0900 -0x07ff}, + {ftExtraBits, 0x0906 -0x07ff}, + {ftOp, 0x091f -0x07ff}, + {ftMemConfig, 0x0939 -0x07ff}, + {ftFastDisable, 0x093c -0x07ff}, + {ftCli, 0x0947 -0x07ff}, + {ftExecLo, 0x0949 -0x07ff}, + {ftExecHi, 0x094a -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64D[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x34, + 0xbd,0x42,0x08,0x9d,0xff,0x01,0xca,0xd0, + 0xf7,0xa2,0xd4,0xbd,0x75,0x08,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x33,0x08,0xce,0x30,0x08,0x88,0xd0, + 0xed,0x4c,0x16,0x01,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0,0x03, + 0xee,0x03,0x02,0x68,0x60,0xe8,0x8a,0x06, + 0xf7,0xd0,0x03,0x20,0x00,0x02,0x90,0x12, + 0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b,0xa2, + 0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20,0x00, + 0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60,0x80, + 0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0,0x02, + 0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2,0x02, + 0x20,0x2f,0x02,0x85,0xf8,0x98,0xa2,0x06, + 0x20,0x2f,0x02,0x20,0xf9,0x00,0xa0,0x00, + 0x8c,0x90,0x01,0x98,0xa2,0x02,0x20,0x2f, + 0x02,0xc5,0xf8,0xd0,0xe9,0x20,0x11,0x02, + 0x85,0x2d,0x4a,0xd0,0x3f,0x20,0x25,0x02, + 0x4a,0x90,0x47,0x20,0x25,0x02,0x4a,0x90, + 0xcb,0xc8,0x20,0x11,0x02,0x85,0x2d,0xc9, + 0x40,0x90,0x0b,0xa2,0x02,0x20,0x26,0x02, + 0x85,0x2d,0x20,0x11,0x02,0xa8,0x20,0x11, + 0x02,0xaa,0xbd,0xba,0x01,0xe0,0x10,0x90, + 0x06,0x8a,0xa2,0x04,0x20,0x26,0x02,0xa6, + 0x2d,0xe8,0x20,0xf9,0x00,0xd0,0xfb,0x88, + 0xd0,0xf8,0xf0,0xaa,0x20,0x11,0x02,0xc9, + 0x7f,0xf0,0x26,0xe9,0x00,0xa2,0x00,0x20, + 0x2f,0x02,0x85,0x2e,0x20,0x23,0x02,0x65, + 0xfa,0xa6,0x2d,0x85,0x2d,0xa5,0xfb,0xe5, + 0x2e,0x85,0x2e,0xe8,0xb1,0x2d,0x18,0x69, + 0x00,0xc8,0x20,0xf9,0x00,0xd0,0xf5,0xf0, + 0xd1,0xa5,0x2d,0xc9,0x02,0xf0,0x09,0x20, + 0x23,0x02,0x8d,0x90,0x01,0x98,0xf0,0xd2, + 0xa9,0x37,0x85,0x01,0xce,0x30,0xd0,0xa5, + 0xfa,0x85,0x2d,0xa5,0xfb,0x85,0x2e,0x58, + 0x4c,0xaa,0xaa,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 +}; + +struct FixEntry fixTableC64D[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftIBufferSize, 0x0816 -0x07ff}, + {ftReloc, 0x0819 -0x07ff}, + {ftStackSize, 0x0821 -0x07ff}, + {ftReloc, 0x0824 -0x07ff}, + {ftSizePages, 0x082c -0x07ff}, + {ftSizeLo, 0x082f -0x07ff}, + {ftSizeHi, 0x0830 -0x07ff}, + {ftEndLo, 0x0832 -0x07ff}, + {ftEndHi, 0x0833 -0x07ff}, + {ftReloc, 0x0839 -0x07ff}, + {ftReloc, 0x083c -0x07ff}, + {ftDeCall, 0x0840 -0x07ff}, + {ftInposLo, 0x0845 -0x07ff}, + {ftInposHi, 0x0846 -0x07ff}, + {ftMaxGamma, 0x0861 -0x07ff}, + {ftEscValue, 0x0877 -0x07ff}, + {ftOutposLo, 0x0879 -0x07ff}, + {ftOutposHi, 0x087a -0x07ff}, + {ftEscBits, 0x0886 -0x07ff}, + {ftEsc8Bits, 0x088e -0x07ff}, + {ftEscBits, 0x089c -0x07ff}, + {ft1MaxGamma, 0x08bf -0x07ff}, + {ft8MaxGamma, 0x08c3 -0x07ff}, + {ft2MaxGamma, 0x08ef -0x07ff}, + {ftExtraBits, 0x08f5 -0x07ff}, + {ftOp, 0x090e -0x07ff}, + {ftMemConfig, 0x0928 -0x07ff}, + {ftFastDisable, 0x092b -0x07ff}, + {ftCli, 0x0936 -0x07ff}, + {ftExecLo, 0x0938 -0x07ff}, + {ftExecHi, 0x0939 -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64WF[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x00, + 0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10,0xf8, + 0xa2,0x3b,0xbd,0x4c,0x08,0x9d,0xff,0x01, + 0xca,0xd0,0xf7,0xa2,0xd6,0xbd,0x86,0x08, + 0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa, + 0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a, + 0xd0,0xf6,0xce,0x3d,0x08,0xce,0x3a,0x08, + 0x88,0xd0,0xed,0x4c,0x18,0x01,0x48,0xad, + 0xaa,0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02, + 0xd0,0x0a,0xee,0x03,0x02,0xd0,0x05,0xa9, + 0x4b,0x8d,0x02,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xf0, + 0x02,0xca,0x60,0xe6,0xfb,0xca,0x60,0xa4, + 0xf8,0xa2,0x02,0x20,0x36,0x02,0x85,0xf8, + 0x98,0xa2,0x06,0x20,0x36,0x02,0x20,0xf9, + 0x00,0xa0,0x00,0x98,0xa2,0x02,0x20,0x36, + 0x02,0xc5,0xf8,0xd0,0xec,0x20,0x18,0x02, + 0x85,0x2d,0x4a,0xd0,0x45,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x4a,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x90,0xc8,0xc8, + 0x20,0x18,0x02,0x85,0x2d,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x2d,0x02,0x85,0x2d, + 0x20,0x18,0x02,0xa8,0x20,0x18,0x02,0xaa, + 0xbd,0xbc,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x2d,0x02,0xa6,0x2d,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xa7,0x20,0x18,0x02,0xc9,0x7f,0xf0, + 0x32,0xe9,0x00,0xa2,0x00,0x20,0x36,0x02, + 0x85,0x2e,0x20,0x2a,0x02,0x65,0xfa,0xa6, + 0x2d,0x8d,0x94,0x01,0xa5,0xfb,0xe5,0x2e, + 0x8d,0x95,0x01,0xe8,0xb9,0xaa,0xaa,0x91, + 0xfa,0xc8,0xca,0xd0,0xf7,0x88,0x98,0x38, + 0x65,0xfa,0x85,0xfa,0x90,0x02,0xe6,0xfb, + 0x4c,0x18,0x01,0xa9,0x37,0x85,0x01,0xce, + 0x30,0xd0,0xa5,0xfa,0x85,0x2d,0xa5,0xfb, + 0x85,0x2e,0x58,0x4c,0xaa,0xaa,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64WF[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftOverlap, 0x0816 -0x07ff}, + {ftOverlapLo, 0x0818 -0x07ff}, + {ftOverlapHi, 0x0819 -0x07ff}, + {ftIBufferSize, 0x0820 -0x07ff}, + {ftReloc, 0x0823 -0x07ff}, + {ftStackSize, 0x082b -0x07ff}, + {ftReloc, 0x082e -0x07ff}, + {ftSizePages, 0x0836 -0x07ff}, + {ftSizeLo, 0x0839 -0x07ff}, + {ftSizeHi, 0x083a -0x07ff}, + {ftEndLo, 0x083c -0x07ff}, + {ftEndHi, 0x083d -0x07ff}, + {ftReloc, 0x0843 -0x07ff}, + {ftReloc, 0x0846 -0x07ff}, + {ftDeCall, 0x084a -0x07ff}, + {ftInposLo, 0x084f -0x07ff}, + {ftInposHi, 0x0850 -0x07ff}, + {ftMaxGamma, 0x0872 -0x07ff}, + {ftEscValue, 0x0888 -0x07ff}, + {ftOutposLo, 0x088a -0x07ff}, + {ftOutposHi, 0x088b -0x07ff}, + {ftEscBits, 0x0899 -0x07ff}, + {ftEsc8Bits, 0x08a1 -0x07ff}, + {ftEscBits, 0x08ac -0x07ff}, + {ft1MaxGamma, 0x08d5 -0x07ff}, + {ft8MaxGamma, 0x08d9 -0x07ff}, + {ft2MaxGamma, 0x0905 -0x07ff}, + {ftExtraBits, 0x090b -0x07ff}, + {ftMemConfig, 0x093b -0x07ff}, + {ftFastDisable, 0x093e -0x07ff}, + {ftCli, 0x0949 -0x07ff}, + {ftExecLo, 0x094b -0x07ff}, + {ftExecHi, 0x094c -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerC64F[] = { + 0x01,0x08,0x0b,0x08,0xef,0x00,0x9e,0x32, + 0x30,0x36,0x31,0x00,0x00,0x00,0x78,0xee, + 0x30,0xd0,0xa9,0x38,0x85,0x01,0xa2,0x34, + 0xbd,0x42,0x08,0x9d,0xff,0x01,0xca,0xd0, + 0xf7,0xa2,0xd6,0xbd,0x75,0x08,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x33,0x08,0xce,0x30,0x08,0x88,0xd0, + 0xed,0x4c,0x18,0x01,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0,0x03, + 0xee,0x03,0x02,0x68,0x60,0xe8,0x8a,0x06, + 0xf7,0xd0,0x03,0x20,0x00,0x02,0x90,0x12, + 0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b,0xa2, + 0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20,0x00, + 0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60,0x80, + 0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xf0,0x02, + 0xca,0x60,0xe6,0xfb,0xca,0x60,0xa4,0xf8, + 0xa2,0x02,0x20,0x2f,0x02,0x85,0xf8,0x98, + 0xa2,0x06,0x20,0x2f,0x02,0x20,0xf9,0x00, + 0xa0,0x00,0x98,0xa2,0x02,0x20,0x2f,0x02, + 0xc5,0xf8,0xd0,0xec,0x20,0x11,0x02,0x85, + 0x2d,0x4a,0xd0,0x45,0x06,0xf7,0xd0,0x03, + 0x20,0x00,0x02,0x90,0x4a,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0xc8,0xc8,0x20, + 0x11,0x02,0x85,0x2d,0xc9,0x40,0x90,0x0b, + 0xa2,0x02,0x20,0x26,0x02,0x85,0x2d,0x20, + 0x11,0x02,0xa8,0x20,0x11,0x02,0xaa,0xbd, + 0xbc,0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2, + 0x04,0x20,0x26,0x02,0xa6,0x2d,0xe8,0x20, + 0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0, + 0xa7,0x20,0x11,0x02,0xc9,0x7f,0xf0,0x32, + 0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02,0x85, + 0x2e,0x20,0x23,0x02,0x65,0xfa,0xa6,0x2d, + 0x8d,0x94,0x01,0xa5,0xfb,0xe5,0x2e,0x8d, + 0x95,0x01,0xe8,0xb9,0xaa,0xaa,0x91,0xfa, + 0xc8,0xca,0xd0,0xf7,0x88,0x98,0x38,0x65, + 0xfa,0x85,0xfa,0x90,0x02,0xe6,0xfb,0x4c, + 0x18,0x01,0xa9,0x37,0x85,0x01,0xce,0x30, + 0xd0,0xa5,0xfa,0x85,0x2d,0xa5,0xfb,0x85, + 0x2e,0x58,0x4c,0xaa,0xaa,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC64F[] = { + {ftFastDisable, 0x080e -0x07ff}, + {ftIBufferSize, 0x0816 -0x07ff}, + {ftReloc, 0x0819 -0x07ff}, + {ftStackSize, 0x0821 -0x07ff}, + {ftReloc, 0x0824 -0x07ff}, + {ftSizePages, 0x082c -0x07ff}, + {ftSizeLo, 0x082f -0x07ff}, + {ftSizeHi, 0x0830 -0x07ff}, + {ftEndLo, 0x0832 -0x07ff}, + {ftEndHi, 0x0833 -0x07ff}, + {ftReloc, 0x0839 -0x07ff}, + {ftReloc, 0x083c -0x07ff}, + {ftDeCall, 0x0840 -0x07ff}, + {ftInposLo, 0x0845 -0x07ff}, + {ftInposHi, 0x0846 -0x07ff}, + {ftMaxGamma, 0x0861 -0x07ff}, + {ftEscValue, 0x0877 -0x07ff}, + {ftOutposLo, 0x0879 -0x07ff}, + {ftOutposHi, 0x087a -0x07ff}, + {ftEscBits, 0x0888 -0x07ff}, + {ftEsc8Bits, 0x0890 -0x07ff}, + {ftEscBits, 0x089b -0x07ff}, + {ft1MaxGamma, 0x08c4 -0x07ff}, + {ft8MaxGamma, 0x08c8 -0x07ff}, + {ft2MaxGamma, 0x08f4 -0x07ff}, + {ftExtraBits, 0x08fa -0x07ff}, + {ftMemConfig, 0x092a -0x07ff}, + {ftFastDisable, 0x092d -0x07ff}, + {ftCli, 0x0938 -0x07ff}, + {ftExecLo, 0x093a -0x07ff}, + {ftExecHi, 0x093b -0x07ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0x78,0xa2, + 0x34,0xbd,0x3b,0x12,0x9d,0xff,0x01,0xca, + 0xd0,0xf7,0xa2,0xb8,0xbd,0x6e,0x12,0x9d, + 0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca, + 0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0, + 0xf6,0xce,0x2c,0x12,0xce,0x29,0x12,0x88, + 0xd0,0xed,0x4c,0x16,0x01,0x48,0xad,0xaa, + 0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0, + 0x03,0xee,0x03,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0, + 0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2, + 0x02,0x20,0x2f,0x02,0x85,0xf8,0x98,0xa2, + 0x06,0x20,0x2f,0x02,0x20,0xf9,0x00,0xa0, + 0x00,0x98,0xa2,0x02,0x20,0x2f,0x02,0xc5, + 0xf8,0xd0,0xec,0x20,0x11,0x02,0x85,0x2d, + 0x4a,0xd0,0x3f,0x20,0x25,0x02,0x4a,0x90, + 0x47,0x20,0x25,0x02,0x4a,0x90,0xce,0xc8, + 0x20,0x11,0x02,0x85,0x2d,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x26,0x02,0x85,0x2d, + 0x20,0x11,0x02,0xa8,0x20,0x11,0x02,0xaa, + 0xbd,0x9e,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x26,0x02,0xa6,0x2d,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xad,0x20,0x11,0x02,0xc9,0x7f,0xf0, + 0x23,0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02, + 0x85,0x2e,0x20,0x23,0x02,0x65,0xfa,0xa6, + 0x2d,0x85,0x2d,0xa5,0xfb,0xe5,0x2e,0x85, + 0x2e,0xe8,0xb1,0x2d,0xc8,0x20,0xf9,0x00, + 0xd0,0xf8,0xf0,0xd4,0xa5,0xfa,0x85,0x2d, + 0xa5,0xfb,0x85,0x2e,0x58,0x4c,0xaa,0xaa, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20[] = { + {ftIBufferSize, 0x120f -0x11ff}, + {ftReloc, 0x1212 -0x11ff}, + {ftStackSize, 0x121a -0x11ff}, + {ftReloc, 0x121d -0x11ff}, + {ftSizePages, 0x1225 -0x11ff}, + {ftSizeLo, 0x1228 -0x11ff}, + {ftSizeHi, 0x1229 -0x11ff}, + {ftEndLo, 0x122b -0x11ff}, + {ftEndHi, 0x122c -0x11ff}, + {ftReloc, 0x1232 -0x11ff}, + {ftReloc, 0x1235 -0x11ff}, + {ftDeCall, 0x1239 -0x11ff}, + {ftInposLo, 0x123e -0x11ff}, + {ftInposHi, 0x123f -0x11ff}, + {ftMaxGamma, 0x125a -0x11ff}, + {ftEscValue, 0x1270 -0x11ff}, + {ftOutposLo, 0x1272 -0x11ff}, + {ftOutposHi, 0x1273 -0x11ff}, + {ftEscBits, 0x127f -0x11ff}, + {ftEsc8Bits, 0x1287 -0x11ff}, + {ftEscBits, 0x1292 -0x11ff}, + {ft1MaxGamma, 0x12b5 -0x11ff}, + {ft8MaxGamma, 0x12b9 -0x11ff}, + {ft2MaxGamma, 0x12e5 -0x11ff}, + {ftExtraBits, 0x12eb -0x11ff}, + {ftCli, 0x1313 -0x11ff}, + {ftExecLo, 0x1315 -0x11ff}, + {ftExecHi, 0x1316 -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20B[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0x78,0xa2, + 0x34,0xbd,0x3b,0x12,0x9d,0xff,0x01,0xca, + 0xd0,0xf7,0xa2,0xbb,0xbd,0x6e,0x12,0x9d, + 0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca, + 0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0, + 0xf6,0xce,0x2c,0x12,0xce,0x29,0x12,0x88, + 0xd0,0xed,0x4c,0x16,0x01,0x48,0xad,0xaa, + 0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0, + 0x03,0xee,0x03,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xd0, + 0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8,0xa2, + 0x02,0x20,0x2f,0x02,0x85,0xf8,0x98,0xa2, + 0x06,0x20,0x2f,0x02,0x20,0xf9,0x00,0xa0, + 0x00,0x98,0xa2,0x02,0x20,0x2f,0x02,0xc5, + 0xf8,0xd0,0xec,0x20,0x11,0x02,0x85,0x2d, + 0x4a,0xd0,0x3f,0x20,0x25,0x02,0x4a,0x90, + 0x47,0x20,0x25,0x02,0x4a,0x90,0xce,0xc8, + 0x20,0x11,0x02,0x85,0x2d,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x26,0x02,0x85,0x2d, + 0x20,0x11,0x02,0xa8,0x20,0x11,0x02,0xaa, + 0xbd,0xa1,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x26,0x02,0xa6,0x2d,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xad,0x20,0x11,0x02,0xc9,0x7f,0xf0, + 0x23,0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02, + 0x85,0x2e,0x20,0x23,0x02,0x65,0xfa,0xa6, + 0x2d,0x85,0x2d,0xa5,0xfb,0xe5,0x2e,0x85, + 0x2e,0xe8,0xb1,0x2d,0xc8,0x20,0xf9,0x00, + 0xd0,0xf8,0xf0,0xd4,0xa5,0xfa,0x85,0x2d, + 0xa5,0xfb,0x85,0x2e,0x58,0x20,0x59,0xc6, + 0x4c,0xae,0xc7,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 +}; + +struct FixEntry fixTableVIC20B[] = { + {ftIBufferSize, 0x120f -0x11ff}, + {ftReloc, 0x1212 -0x11ff}, + {ftStackSize, 0x121a -0x11ff}, + {ftReloc, 0x121d -0x11ff}, + {ftSizePages, 0x1225 -0x11ff}, + {ftSizeLo, 0x1228 -0x11ff}, + {ftSizeHi, 0x1229 -0x11ff}, + {ftEndLo, 0x122b -0x11ff}, + {ftEndHi, 0x122c -0x11ff}, + {ftReloc, 0x1232 -0x11ff}, + {ftReloc, 0x1235 -0x11ff}, + {ftDeCall, 0x1239 -0x11ff}, + {ftInposLo, 0x123e -0x11ff}, + {ftInposHi, 0x123f -0x11ff}, + {ftMaxGamma, 0x125a -0x11ff}, + {ftEscValue, 0x1270 -0x11ff}, + {ftOutposLo, 0x1272 -0x11ff}, + {ftOutposHi, 0x1273 -0x11ff}, + {ftEscBits, 0x127f -0x11ff}, + {ftEsc8Bits, 0x1287 -0x11ff}, + {ftEscBits, 0x1292 -0x11ff}, + {ft1MaxGamma, 0x12b5 -0x11ff}, + {ft8MaxGamma, 0x12b9 -0x11ff}, + {ft2MaxGamma, 0x12e5 -0x11ff}, + {ftExtraBits, 0x12eb -0x11ff}, + {ftCli, 0x1313 -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20W[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0x78,0xa2, + 0x00,0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10, + 0xf8,0xa2,0x42,0xbd,0x45,0x12,0x9d,0xff, + 0x01,0xca,0xd0,0xf7,0xa2,0xb9,0xbd,0x86, + 0x12,0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0, + 0xaa,0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff, + 0x8a,0xd0,0xf6,0xce,0x36,0x12,0xce,0x33, + 0x12,0x88,0xd0,0xed,0x4c,0x17,0x01,0x48, + 0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee,0x02, + 0x02,0xd0,0x11,0xee,0x03,0x02,0xc6,0xf9, + 0xd0,0x0a,0xa9,0x4b,0x8d,0x02,0x02,0xa9, + 0x00,0x8d,0x03,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0xff,0x8d,0xaa,0xaa,0xe6,0xfb, + 0xd0,0x02,0xe6,0xfc,0xca,0x60,0xa4,0xf8, + 0xa2,0x02,0x20,0x3d,0x02,0x85,0xf8,0x98, + 0xa2,0x06,0x20,0x3d,0x02,0x20,0xfa,0x00, + 0xa0,0x00,0x98,0xa2,0x02,0x20,0x3d,0x02, + 0xc5,0xf8,0xd0,0xec,0x20,0x1f,0x02,0x85, + 0x2d,0x4a,0xd0,0x3f,0x20,0x33,0x02,0x4a, + 0x90,0x47,0x20,0x33,0x02,0x4a,0x90,0xce, + 0xc8,0x20,0x1f,0x02,0x85,0x2d,0xc9,0x40, + 0x90,0x0b,0xa2,0x02,0x20,0x34,0x02,0x85, + 0x2d,0x20,0x1f,0x02,0xa8,0x20,0x1f,0x02, + 0xaa,0xbd,0x9f,0x01,0xe0,0x10,0x90,0x06, + 0x8a,0xa2,0x04,0x20,0x34,0x02,0xa6,0x2d, + 0xe8,0x20,0xfa,0x00,0xd0,0xfb,0x88,0xd0, + 0xf8,0xf0,0xad,0x20,0x1f,0x02,0xc9,0x7f, + 0xf0,0x23,0xe9,0x00,0xa2,0x00,0x20,0x3d, + 0x02,0x85,0x2e,0x20,0x31,0x02,0x65,0xfb, + 0xa6,0x2d,0x85,0x2d,0xa5,0xfc,0xe5,0x2e, + 0x85,0x2e,0xe8,0xb1,0x2d,0xc8,0x20,0xfa, + 0x00,0xd0,0xf8,0xf0,0xd4,0xa5,0xfb,0x85, + 0x2d,0xa5,0xfc,0x85,0x2e,0x58,0x4c,0xaa, + 0xaa,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20W[] = { + {ftOverlap, 0x120f -0x11ff}, + {ftOverlapLo, 0x1211 -0x11ff}, + {ftOverlapHi, 0x1212 -0x11ff}, + {ftIBufferSize, 0x1219 -0x11ff}, + {ftReloc, 0x121c -0x11ff}, + {ftStackSize, 0x1224 -0x11ff}, + {ftReloc, 0x1227 -0x11ff}, + {ftSizePages, 0x122f -0x11ff}, + {ftSizeLo, 0x1232 -0x11ff}, + {ftSizeHi, 0x1233 -0x11ff}, + {ftEndLo, 0x1235 -0x11ff}, + {ftEndHi, 0x1236 -0x11ff}, + {ftReloc, 0x123c -0x11ff}, + {ftReloc, 0x123f -0x11ff}, + {ftDeCall, 0x1243 -0x11ff}, + {ftInposLo, 0x1248 -0x11ff}, + {ftInposHi, 0x1249 -0x11ff}, + {ftMaxGamma, 0x1272 -0x11ff}, + {ftEscValue, 0x1288 -0x11ff}, + {ftWrapCount, 0x1289 -0x11ff}, + {ftOutposLo, 0x128b -0x11ff}, + {ftOutposHi, 0x128c -0x11ff}, + {ftEscBits, 0x1298 -0x11ff}, + {ftEsc8Bits, 0x12a0 -0x11ff}, + {ftEscBits, 0x12ab -0x11ff}, + {ft1MaxGamma, 0x12ce -0x11ff}, + {ft8MaxGamma, 0x12d2 -0x11ff}, + {ft2MaxGamma, 0x12fe -0x11ff}, + {ftExtraBits, 0x1304 -0x11ff}, + {ftCli, 0x132c -0x11ff}, + {ftExecLo, 0x132e -0x11ff}, + {ftExecHi, 0x132f -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20S[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0xa9,0xaa, + 0x85,0x2d,0xa9,0xaa,0x85,0x2e,0xa2,0xdb, + 0xbd,0x37,0x12,0x9d,0xf6,0x00,0xca,0xd0, + 0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa,0x9d, + 0x00,0xff,0x8a,0xd0,0xf6,0xce,0x28,0x12, + 0xce,0x25,0x12,0x88,0xd0,0xed,0x4c,0x16, + 0x01,0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa, + 0xd0,0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8, + 0xa2,0x02,0x20,0xbe,0x01,0x85,0xf8,0x98, + 0xa2,0x06,0x20,0xbe,0x01,0x20,0xf9,0x00, + 0xa0,0x00,0x98,0xa2,0x02,0x20,0xbe,0x01, + 0xc5,0xf8,0xd0,0xec,0x20,0xab,0x01,0x85, + 0x2f,0x4a,0xd0,0x3d,0x20,0x96,0x01,0x90, + 0x46,0x20,0x96,0x01,0x90,0xd0,0xc8,0x20, + 0xab,0x01,0x85,0x2f,0xc9,0x40,0x90,0x0b, + 0xa2,0x02,0x20,0xb9,0x01,0x85,0x2f,0x20, + 0xab,0x01,0xa8,0x20,0xab,0x01,0xaa,0xbd, + 0xc1,0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2, + 0x04,0x20,0xb9,0x01,0xa6,0x2f,0xe8,0x20, + 0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0, + 0xaf,0x20,0xab,0x01,0xc9,0x7f,0xf0,0x25, + 0xe9,0x00,0xa2,0x00,0x20,0xbe,0x01,0x85, + 0x30,0xa2,0x08,0x20,0xb9,0x01,0x65,0xfa, + 0xa6,0x2f,0x85,0x2f,0xa5,0xfb,0xe5,0x30, + 0x85,0x30,0xe8,0xb1,0x2f,0xc8,0x20,0xf9, + 0x00,0xd0,0xf8,0xf0,0xd2,0x4c,0xaa,0xaa, + 0x06,0xf7,0xd0,0x10,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x9c,0x01,0xd0,0x03, + 0xee,0x9d,0x01,0x68,0x60,0xe8,0x8a,0x20, + 0x96,0x01,0x90,0x0b,0xe8,0xe0,0x07,0xd0, + 0xf6,0xf0,0x04,0x20,0x96,0x01,0x2a,0xca, + 0xd0,0xf9,0x18,0x60,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20S[] = { + {ftBEndHi, 0x1212 -0x11ff}, + {ftStackSize, 0x1216 -0x11ff}, + {ftReloc, 0x1219 -0x11ff}, + {ftSizePages, 0x1221 -0x11ff}, + {ftSizeLo, 0x1224 -0x11ff}, + {ftSizeHi, 0x1225 -0x11ff}, + {ftEndLo, 0x1227 -0x11ff}, + {ftEndHi, 0x1228 -0x11ff}, + {ftReloc, 0x122e -0x11ff}, + {ftReloc, 0x1231 -0x11ff}, + {ftDeCall, 0x1235 -0x11ff}, + {ftEscValue, 0x1239 -0x11ff}, + {ftOutposLo, 0x123b -0x11ff}, + {ftOutposHi, 0x123c -0x11ff}, + {ftEscBits, 0x1248 -0x11ff}, + {ftEsc8Bits, 0x1250 -0x11ff}, + {ftEscBits, 0x125b -0x11ff}, + {ft1MaxGamma, 0x127c -0x11ff}, + {ft8MaxGamma, 0x1280 -0x11ff}, + {ft2MaxGamma, 0x12ac -0x11ff}, + {ftExtraBits, 0x12b2 -0x11ff}, + {ftExecLo, 0x12d5 -0x11ff}, + {ftExecHi, 0x12d6 -0x11ff}, + {ftInposLo, 0x12dd -0x11ff}, + {ftInposHi, 0x12de -0x11ff}, + {ftMaxGamma, 0x12f5 -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20SB[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0xa9,0xaa, + 0x85,0x2d,0xa9,0xaa,0x85,0x2e,0xa2,0xe0, + 0xbd,0x37,0x12,0x9d,0xf6,0x00,0xca,0xd0, + 0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa,0x9d, + 0x00,0xff,0x8a,0xd0,0xf6,0xce,0x28,0x12, + 0xce,0x25,0x12,0x88,0xd0,0xed,0x4c,0x16, + 0x01,0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa, + 0xd0,0x02,0xe6,0xfb,0xca,0x60,0xa4,0xf8, + 0xa2,0x02,0x20,0xc3,0x01,0x85,0xf8,0x98, + 0xa2,0x06,0x20,0xc3,0x01,0x20,0xf9,0x00, + 0xa0,0x00,0x98,0xa2,0x02,0x20,0xc3,0x01, + 0xc5,0xf8,0xd0,0xec,0x20,0xb0,0x01,0x85, + 0x2f,0x4a,0xd0,0x3d,0x20,0x9b,0x01,0x90, + 0x46,0x20,0x9b,0x01,0x90,0xd0,0xc8,0x20, + 0xb0,0x01,0x85,0x2f,0xc9,0x40,0x90,0x0b, + 0xa2,0x02,0x20,0xbe,0x01,0x85,0x2f,0x20, + 0xb0,0x01,0xa8,0x20,0xb0,0x01,0xaa,0xbd, + 0xc6,0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2, + 0x04,0x20,0xbe,0x01,0xa6,0x2f,0xe8,0x20, + 0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0, + 0xaf,0x20,0xb0,0x01,0xc9,0x7f,0xf0,0x25, + 0xe9,0x00,0xa2,0x00,0x20,0xc3,0x01,0x85, + 0x30,0xa2,0x08,0x20,0xbe,0x01,0x65,0xfa, + 0xa6,0x2f,0x85,0x2f,0xa5,0xfb,0xe5,0x30, + 0x85,0x30,0xe8,0xb1,0x2f,0xc8,0x20,0xf9, + 0x00,0xd0,0xf8,0xf0,0xd2,0xa9,0x00,0x20, + 0x71,0xc8,0x4c,0xae,0xc7,0x06,0xf7,0xd0, + 0x10,0x48,0xad,0xaa,0xaa,0x2a,0x85,0xf7, + 0xee,0xa1,0x01,0xd0,0x03,0xee,0xa2,0x01, + 0x68,0x60,0xe8,0x8a,0x20,0x9b,0x01,0x90, + 0x0b,0xe8,0xe0,0x07,0xd0,0xf6,0xf0,0x04, + 0x20,0x9b,0x01,0x2a,0xca,0xd0,0xf9,0x18, + 0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20SB[] = { + {ftBEndHi, 0x1212 -0x11ff}, + {ftStackSize, 0x1216 -0x11ff}, + {ftReloc, 0x1219 -0x11ff}, + {ftSizePages, 0x1221 -0x11ff}, + {ftSizeLo, 0x1224 -0x11ff}, + {ftSizeHi, 0x1225 -0x11ff}, + {ftEndLo, 0x1227 -0x11ff}, + {ftEndHi, 0x1228 -0x11ff}, + {ftReloc, 0x122e -0x11ff}, + {ftReloc, 0x1231 -0x11ff}, + {ftDeCall, 0x1235 -0x11ff}, + {ftEscValue, 0x1239 -0x11ff}, + {ftOutposLo, 0x123b -0x11ff}, + {ftOutposHi, 0x123c -0x11ff}, + {ftEscBits, 0x1248 -0x11ff}, + {ftEsc8Bits, 0x1250 -0x11ff}, + {ftEscBits, 0x125b -0x11ff}, + {ft1MaxGamma, 0x127c -0x11ff}, + {ft8MaxGamma, 0x1280 -0x11ff}, + {ft2MaxGamma, 0x12ac -0x11ff}, + {ftExtraBits, 0x12b2 -0x11ff}, + {ftInposLo, 0x12e2 -0x11ff}, + {ftInposHi, 0x12e3 -0x11ff}, + {ftMaxGamma, 0x12fa -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20SW[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0xa9,0xaa, + 0x85,0x2d,0xa9,0xaa,0x85,0x2e,0xa2,0x00, + 0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10,0xf8, + 0x9a,0xa2,0xea,0xbd,0x42,0x12,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x33,0x12,0xce,0x30,0x12,0x88,0xd0, + 0xed,0x4c,0x17,0x01,0x80,0x00,0xff,0x8d, + 0xaa,0xaa,0xe6,0xfb,0xd0,0x02,0xe6,0xfc, + 0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20,0xcd, + 0x01,0x85,0xf8,0x98,0xa2,0x06,0x20,0xcd, + 0x01,0x20,0xfa,0x00,0xa0,0x00,0x98,0xa2, + 0x02,0x20,0xcd,0x01,0xc5,0xf8,0xd0,0xec, + 0x20,0xba,0x01,0x85,0x2f,0x4a,0xd0,0x3d, + 0x20,0x97,0x01,0x90,0x46,0x20,0x97,0x01, + 0x90,0xd0,0xc8,0x20,0xba,0x01,0x85,0x2f, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0xc8, + 0x01,0x85,0x2f,0x20,0xba,0x01,0xa8,0x20, + 0xba,0x01,0xaa,0xbd,0xd0,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0xc8,0x01, + 0xa6,0x2f,0xe8,0x20,0xfa,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xaf,0x20,0xba,0x01, + 0xc9,0x7f,0xf0,0x25,0xe9,0x00,0xa2,0x00, + 0x20,0xcd,0x01,0x85,0x30,0xa2,0x08,0x20, + 0xc8,0x01,0x65,0xfb,0xa6,0x2f,0x85,0x2f, + 0xa5,0xfc,0xe5,0x30,0x85,0x30,0xe8,0xb1, + 0x2f,0xc8,0x20,0xfa,0x00,0xd0,0xf8,0xf0, + 0xd2,0x4c,0xaa,0xaa,0x06,0xf7,0xd0,0x1e, + 0x48,0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee, + 0x9d,0x01,0xd0,0x11,0xee,0x9e,0x01,0xc6, + 0xf9,0xd0,0x0a,0xa9,0x4b,0x8d,0x9d,0x01, + 0xa9,0x00,0x8d,0x9e,0x01,0x68,0x60,0xe8, + 0x8a,0x20,0x97,0x01,0x90,0x0b,0xe8,0xe0, + 0x07,0xd0,0xf6,0xf0,0x04,0x20,0x97,0x01, + 0x2a,0xca,0xd0,0xf9,0x18,0x60,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20SW[] = { + {ftBEndHi, 0x1212 -0x11ff}, + {ftOverlap, 0x1216 -0x11ff}, + {ftOverlapLo, 0x1218 -0x11ff}, + {ftOverlapHi, 0x1219 -0x11ff}, + {ftStackSize, 0x1221 -0x11ff}, + {ftReloc, 0x1224 -0x11ff}, + {ftSizePages, 0x122c -0x11ff}, + {ftSizeLo, 0x122f -0x11ff}, + {ftSizeHi, 0x1230 -0x11ff}, + {ftEndLo, 0x1232 -0x11ff}, + {ftEndHi, 0x1233 -0x11ff}, + {ftReloc, 0x1239 -0x11ff}, + {ftReloc, 0x123c -0x11ff}, + {ftDeCall, 0x1240 -0x11ff}, + {ftEscValue, 0x1244 -0x11ff}, + {ftWrapCount, 0x1245 -0x11ff}, + {ftOutposLo, 0x1247 -0x11ff}, + {ftOutposHi, 0x1248 -0x11ff}, + {ftEscBits, 0x1254 -0x11ff}, + {ftEsc8Bits, 0x125c -0x11ff}, + {ftEscBits, 0x1267 -0x11ff}, + {ft1MaxGamma, 0x1288 -0x11ff}, + {ft8MaxGamma, 0x128c -0x11ff}, + {ft2MaxGamma, 0x12b8 -0x11ff}, + {ftExtraBits, 0x12be -0x11ff}, + {ftExecLo, 0x12e1 -0x11ff}, + {ftExecHi, 0x12e2 -0x11ff}, + {ftInposLo, 0x12e9 -0x11ff}, + {ftInposHi, 0x12ea -0x11ff}, + {ftMaxGamma, 0x130f -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20WD[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0x78,0xa2, + 0x00,0xbd,0xaa,0xaa,0x95,0x4b,0xca,0x10, + 0xf8,0xa2,0x42,0xbd,0x45,0x12,0x9d,0xff, + 0x01,0xca,0xd0,0xf7,0xa2,0xce,0xbd,0x86, + 0x12,0x9d,0xf6,0x00,0xca,0xd0,0xf7,0xa0, + 0xaa,0xca,0xbd,0xaa,0xaa,0x9d,0x00,0xff, + 0x8a,0xd0,0xf6,0xce,0x36,0x12,0xce,0x33, + 0x12,0x88,0xd0,0xed,0x4c,0x17,0x01,0x48, + 0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee,0x02, + 0x02,0xd0,0x11,0xee,0x03,0x02,0xc6,0xf9, + 0xd0,0x0a,0xa9,0x4b,0x8d,0x02,0x02,0xa9, + 0x00,0x8d,0x03,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0xff,0x8d,0xaa,0xaa,0xe6,0xfb, + 0xd0,0x02,0xe6,0xfc,0xca,0x60,0xa4,0xf8, + 0xa2,0x02,0x20,0x3d,0x02,0x85,0xf8,0x98, + 0xa2,0x06,0x20,0x3d,0x02,0x20,0xfa,0x00, + 0xa0,0x00,0x8c,0x91,0x01,0x98,0xa2,0x02, + 0x20,0x3d,0x02,0xc5,0xf8,0xd0,0xe9,0x20, + 0x1f,0x02,0x85,0x2d,0x4a,0xd0,0x3f,0x20, + 0x33,0x02,0x4a,0x90,0x47,0x20,0x33,0x02, + 0x4a,0x90,0xcb,0xc8,0x20,0x1f,0x02,0x85, + 0x2d,0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20, + 0x34,0x02,0x85,0x2d,0x20,0x1f,0x02,0xa8, + 0x20,0x1f,0x02,0xaa,0xbd,0xb4,0x01,0xe0, + 0x10,0x90,0x06,0x8a,0xa2,0x04,0x20,0x34, + 0x02,0xa6,0x2d,0xe8,0x20,0xfa,0x00,0xd0, + 0xfb,0x88,0xd0,0xf8,0xf0,0xaa,0x20,0x1f, + 0x02,0xc9,0x7f,0xf0,0x26,0xe9,0x00,0xa2, + 0x00,0x20,0x3d,0x02,0x85,0x2e,0x20,0x31, + 0x02,0x65,0xfb,0xa6,0x2d,0x85,0x2d,0xa5, + 0xfc,0xe5,0x2e,0x85,0x2e,0xe8,0xb1,0x2d, + 0x18,0x69,0x00,0xc8,0x20,0xfa,0x00,0xd0, + 0xf5,0xf0,0xd1,0xa5,0x2d,0xc9,0x02,0xf0, + 0x09,0x20,0x31,0x02,0x8d,0x91,0x01,0x98, + 0xf0,0xd2,0xa5,0xfb,0x85,0x2d,0xa5,0xfc, + 0x85,0x2e,0x58,0x4c,0xaa,0xaa,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableVIC20WD[] = { + {ftOverlap, 0x120f -0x11ff}, + {ftOverlapLo, 0x1211 -0x11ff}, + {ftOverlapHi, 0x1212 -0x11ff}, + {ftIBufferSize, 0x1219 -0x11ff}, + {ftReloc, 0x121c -0x11ff}, + {ftStackSize, 0x1224 -0x11ff}, + {ftReloc, 0x1227 -0x11ff}, + {ftSizePages, 0x122f -0x11ff}, + {ftSizeLo, 0x1232 -0x11ff}, + {ftSizeHi, 0x1233 -0x11ff}, + {ftEndLo, 0x1235 -0x11ff}, + {ftEndHi, 0x1236 -0x11ff}, + {ftReloc, 0x123c -0x11ff}, + {ftReloc, 0x123f -0x11ff}, + {ftDeCall, 0x1243 -0x11ff}, + {ftInposLo, 0x1248 -0x11ff}, + {ftInposHi, 0x1249 -0x11ff}, + {ftMaxGamma, 0x1272 -0x11ff}, + {ftEscValue, 0x1288 -0x11ff}, + {ftWrapCount, 0x1289 -0x11ff}, + {ftOutposLo, 0x128b -0x11ff}, + {ftOutposHi, 0x128c -0x11ff}, + {ftEscBits, 0x1298 -0x11ff}, + {ftEsc8Bits, 0x12a0 -0x11ff}, + {ftEscBits, 0x12ae -0x11ff}, + {ft1MaxGamma, 0x12d1 -0x11ff}, + {ft8MaxGamma, 0x12d5 -0x11ff}, + {ft2MaxGamma, 0x1301 -0x11ff}, + {ftExtraBits, 0x1307 -0x11ff}, + {ftOp, 0x1320 -0x11ff}, + {ftCli, 0x1341 -0x11ff}, + {ftExecLo, 0x1343 -0x11ff}, + {ftExecHi, 0x1344 -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerVIC20FB[] = { + 0x01,0x12,0x0b,0x12,0xef,0x00,0x9e,0x34, + 0x36,0x32,0x31,0x00,0x00,0x00,0x78,0xa2, + 0x34,0xbd,0x3b,0x12,0x9d,0xff,0x01,0xca, + 0xd0,0xf7,0xa2,0xd2,0xbd,0x6e,0x12,0x9d, + 0xf6,0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca, + 0xbd,0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0, + 0xf6,0xce,0x2c,0x12,0xce,0x29,0x12,0x88, + 0xd0,0xed,0x4c,0x18,0x01,0x48,0xad,0xaa, + 0xaa,0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0, + 0x03,0xee,0x03,0x02,0x68,0x60,0xe8,0x8a, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x90, + 0x12,0xe8,0xe0,0x07,0xd0,0xf2,0xf0,0x0b, + 0xa2,0x07,0xe8,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x2a,0xca,0xd0,0xf5,0x18,0x60, + 0x80,0x00,0x8d,0xaa,0xaa,0xe6,0xfa,0xf0, + 0x02,0xca,0x60,0xe6,0xfb,0xca,0x60,0xa4, + 0xf8,0xa2,0x02,0x20,0x2f,0x02,0x85,0xf8, + 0x98,0xa2,0x06,0x20,0x2f,0x02,0x20,0xf9, + 0x00,0xa0,0x00,0x98,0xa2,0x02,0x20,0x2f, + 0x02,0xc5,0xf8,0xd0,0xec,0x20,0x11,0x02, + 0x85,0x2d,0x4a,0xd0,0x45,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x4a,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x90,0xc8,0xc8, + 0x20,0x11,0x02,0x85,0x2d,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x26,0x02,0x85,0x2d, + 0x20,0x11,0x02,0xa8,0x20,0x11,0x02,0xaa, + 0xbd,0xb8,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x26,0x02,0xa6,0x2d,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xa7,0x20,0x11,0x02,0xc9,0x7f,0xf0, + 0x32,0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02, + 0x85,0x2e,0x20,0x23,0x02,0x65,0xfa,0xa6, + 0x2d,0x8d,0x94,0x01,0xa5,0xfb,0xe5,0x2e, + 0x8d,0x95,0x01,0xe8,0xb9,0xaa,0xaa,0x91, + 0xfa,0xc8,0xca,0xd0,0xf7,0x88,0x98,0x38, + 0x65,0xfa,0x85,0xfa,0x90,0x02,0xe6,0xfb, + 0x4c,0x18,0x01,0xa5,0xfa,0x85,0x2d,0xa5, + 0xfb,0x85,0x2e,0x58,0x20,0x59,0xc6,0x4c, + 0xae,0xc7,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00 +}; + +struct FixEntry fixTableVIC20FB[] = { + {ftIBufferSize, 0x120f -0x11ff}, + {ftReloc, 0x1212 -0x11ff}, + {ftStackSize, 0x121a -0x11ff}, + {ftReloc, 0x121d -0x11ff}, + {ftSizePages, 0x1225 -0x11ff}, + {ftSizeLo, 0x1228 -0x11ff}, + {ftSizeHi, 0x1229 -0x11ff}, + {ftEndLo, 0x122b -0x11ff}, + {ftEndHi, 0x122c -0x11ff}, + {ftReloc, 0x1232 -0x11ff}, + {ftReloc, 0x1235 -0x11ff}, + {ftDeCall, 0x1239 -0x11ff}, + {ftInposLo, 0x123e -0x11ff}, + {ftInposHi, 0x123f -0x11ff}, + {ftMaxGamma, 0x125a -0x11ff}, + {ftEscValue, 0x1270 -0x11ff}, + {ftOutposLo, 0x1272 -0x11ff}, + {ftOutposHi, 0x1273 -0x11ff}, + {ftEscBits, 0x1281 -0x11ff}, + {ftEsc8Bits, 0x1289 -0x11ff}, + {ftEscBits, 0x1294 -0x11ff}, + {ft1MaxGamma, 0x12bd -0x11ff}, + {ft8MaxGamma, 0x12c1 -0x11ff}, + {ft2MaxGamma, 0x12ed -0x11ff}, + {ftExtraBits, 0x12f3 -0x11ff}, + {ftCli, 0x132a -0x11ff}, + {ftEnd,0} +}; + +static unsigned char headerC16[] = { + 0x01,0x10,0x0b,0x10,0xef,0x00,0x9e,0x34, + 0x31,0x30,0x39,0x00,0x00,0x00,0x78,0xad, + 0x06,0xff,0x29,0xef,0x8d,0x06,0xff,0x8d, + 0x3f,0xff,0xa2,0x34,0xbd,0x46,0x10,0x9d, + 0xff,0x01,0xca,0xd0,0xf7,0xa2,0xc7,0xbd, + 0x79,0x10,0x9d,0xf6,0x00,0xca,0xd0,0xf7, + 0xa0,0xaa,0xca,0xbd,0xaa,0xaa,0x9d,0x00, + 0xff,0x8a,0xd0,0xf6,0xce,0x37,0x10,0xce, + 0x34,0x10,0x88,0xd0,0xed,0x4c,0x16,0x01, + 0x48,0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee, + 0x02,0x02,0xd0,0x03,0xee,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0x8d,0xaa,0xaa, + 0xe6,0xfa,0xd0,0x02,0xe6,0xfb,0xca,0x60, + 0xa4,0xf8,0xa2,0x02,0x20,0x2f,0x02,0x85, + 0xf8,0x98,0xa2,0x06,0x20,0x2f,0x02,0x20, + 0xf9,0x00,0xa0,0x00,0x98,0xa2,0x02,0x20, + 0x2f,0x02,0xc5,0xf8,0xd0,0xec,0x20,0x11, + 0x02,0x85,0x2d,0x4a,0xd0,0x3f,0x20,0x25, + 0x02,0x4a,0x90,0x47,0x20,0x25,0x02,0x4a, + 0x90,0xce,0xc8,0x20,0x11,0x02,0x85,0x2d, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0x26, + 0x02,0x85,0x2d,0x20,0x11,0x02,0xa8,0x20, + 0x11,0x02,0xaa,0xbd,0xad,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0x26,0x02, + 0xa6,0x2d,0xe8,0x20,0xf9,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xad,0x20,0x11,0x02, + 0xc9,0x7f,0xf0,0x23,0xe9,0x00,0xa2,0x00, + 0x20,0x2f,0x02,0x85,0x2e,0x20,0x23,0x02, + 0x65,0xfa,0xa6,0x2d,0x85,0x2d,0xa5,0xfb, + 0xe5,0x2e,0x85,0x2e,0xe8,0xb1,0x2d,0xc8, + 0x20,0xf9,0x00,0xd0,0xf8,0xf0,0xd4,0xa5, + 0xfa,0x85,0x2d,0xa5,0xfb,0x85,0x2e,0xad, + 0x06,0xff,0x09,0x10,0x8d,0x06,0xff,0x8d, + 0x3e,0xff,0xa9,0x00,0x85,0xf9,0x58,0x4c, + 0xaa,0xaa,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00 +}; + +struct FixEntry fixTableC16[] = { + {ftFastDisable, 0x1013 -0x0fff}, + {ftIBufferSize, 0x101a -0x0fff}, + {ftReloc, 0x101d -0x0fff}, + {ftStackSize, 0x1025 -0x0fff}, + {ftReloc, 0x1028 -0x0fff}, + {ftSizePages, 0x1030 -0x0fff}, + {ftSizeLo, 0x1033 -0x0fff}, + {ftSizeHi, 0x1034 -0x0fff}, + {ftEndLo, 0x1036 -0x0fff}, + {ftEndHi, 0x1037 -0x0fff}, + {ftReloc, 0x103d -0x0fff}, + {ftReloc, 0x1040 -0x0fff}, + {ftDeCall, 0x1044 -0x0fff}, + {ftInposLo, 0x1049 -0x0fff}, + {ftInposHi, 0x104a -0x0fff}, + {ftMaxGamma, 0x1065 -0x0fff}, + {ftEscValue, 0x107b -0x0fff}, + {ftOutposLo, 0x107d -0x0fff}, + {ftOutposHi, 0x107e -0x0fff}, + {ftEscBits, 0x108a -0x0fff}, + {ftEsc8Bits, 0x1092 -0x0fff}, + {ftEscBits, 0x109d -0x0fff}, + {ft1MaxGamma, 0x10c0 -0x0fff}, + {ft8MaxGamma, 0x10c4 -0x0fff}, + {ft2MaxGamma, 0x10f0 -0x0fff}, + {ftExtraBits, 0x10f6 -0x0fff}, + {ftFastDisable, 0x1123 -0x0fff}, + {ftCli, 0x112d -0x0fff}, + {ftExecLo, 0x112f -0x0fff}, + {ftExecHi, 0x1130 -0x0fff}, + {ftEnd,0} +}; + +static unsigned char headerC16W[] = { + 0x01,0x10,0x0b,0x10,0xef,0x00,0x9e,0x34, + 0x31,0x30,0x39,0x00,0x00,0x00,0x78,0xad, + 0x06,0xff,0x29,0xef,0x8d,0x06,0xff,0x8d, + 0x3f,0xff,0xa2,0x00,0xbd,0xaa,0xaa,0x95, + 0x4b,0xca,0x10,0xf8,0xa2,0x42,0xbd,0x50, + 0x10,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xc8,0xbd,0x91,0x10,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x41, + 0x10,0xce,0x3e,0x10,0x88,0xd0,0xed,0x4c, + 0x17,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x11,0xee,0x03, + 0x02,0xc6,0xf9,0xd0,0x0a,0xa9,0x4b,0x8d, + 0x02,0x02,0xa9,0x00,0x8d,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0xff,0x8d,0xaa, + 0xaa,0xe6,0xfb,0xd0,0x02,0xe6,0xfc,0xca, + 0x60,0xa4,0xf8,0xa2,0x02,0x20,0x3d,0x02, + 0x85,0xf8,0x98,0xa2,0x06,0x20,0x3d,0x02, + 0x20,0xfa,0x00,0xa0,0x00,0x98,0xa2,0x02, + 0x20,0x3d,0x02,0xc5,0xf8,0xd0,0xec,0x20, + 0x1f,0x02,0x85,0x2d,0x4a,0xd0,0x3f,0x20, + 0x33,0x02,0x4a,0x90,0x47,0x20,0x33,0x02, + 0x4a,0x90,0xce,0xc8,0x20,0x1f,0x02,0x85, + 0x2d,0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20, + 0x34,0x02,0x85,0x2d,0x20,0x1f,0x02,0xa8, + 0x20,0x1f,0x02,0xaa,0xbd,0xae,0x01,0xe0, + 0x10,0x90,0x06,0x8a,0xa2,0x04,0x20,0x34, + 0x02,0xa6,0x2d,0xe8,0x20,0xfa,0x00,0xd0, + 0xfb,0x88,0xd0,0xf8,0xf0,0xad,0x20,0x1f, + 0x02,0xc9,0x7f,0xf0,0x23,0xe9,0x00,0xa2, + 0x00,0x20,0x3d,0x02,0x85,0x2e,0x20,0x31, + 0x02,0x65,0xfb,0xa6,0x2d,0x85,0x2d,0xa5, + 0xfc,0xe5,0x2e,0x85,0x2e,0xe8,0xb1,0x2d, + 0xc8,0x20,0xfa,0x00,0xd0,0xf8,0xf0,0xd4, + 0xa5,0xfb,0x85,0x2d,0xa5,0xfc,0x85,0x2e, + 0xad,0x06,0xff,0x09,0x10,0x8d,0x06,0xff, + 0x8d,0x3e,0xff,0xa9,0x00,0x85,0xf9,0x58, + 0x4c,0xaa,0xaa,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 +}; + +struct FixEntry fixTableC16W[] = { + {ftFastDisable, 0x1013 -0x0fff}, + {ftOverlap, 0x101a -0x0fff}, + {ftOverlapLo, 0x101c -0x0fff}, + {ftOverlapHi, 0x101d -0x0fff}, + {ftIBufferSize, 0x1024 -0x0fff}, + {ftReloc, 0x1027 -0x0fff}, + {ftStackSize, 0x102f -0x0fff}, + {ftReloc, 0x1032 -0x0fff}, + {ftSizePages, 0x103a -0x0fff}, + {ftSizeLo, 0x103d -0x0fff}, + {ftSizeHi, 0x103e -0x0fff}, + {ftEndLo, 0x1040 -0x0fff}, + {ftEndHi, 0x1041 -0x0fff}, + {ftReloc, 0x1047 -0x0fff}, + {ftReloc, 0x104a -0x0fff}, + {ftDeCall, 0x104e -0x0fff}, + {ftInposLo, 0x1053 -0x0fff}, + {ftInposHi, 0x1054 -0x0fff}, + {ftMaxGamma, 0x107d -0x0fff}, + {ftEscValue, 0x1093 -0x0fff}, + {ftWrapCount, 0x1094 -0x0fff}, + {ftOutposLo, 0x1096 -0x0fff}, + {ftOutposHi, 0x1097 -0x0fff}, + {ftEscBits, 0x10a3 -0x0fff}, + {ftEsc8Bits, 0x10ab -0x0fff}, + {ftEscBits, 0x10b6 -0x0fff}, + {ft1MaxGamma, 0x10d9 -0x0fff}, + {ft8MaxGamma, 0x10dd -0x0fff}, + {ft2MaxGamma, 0x1109 -0x0fff}, + {ftExtraBits, 0x110f -0x0fff}, + {ftFastDisable, 0x113c -0x0fff}, + {ftCli, 0x1146 -0x0fff}, + {ftExecLo, 0x1148 -0x0fff}, + {ftExecHi, 0x1149 -0x0fff}, + {ftEnd,0} +}; + +static unsigned char headerC16WD[] = { + 0x01,0x10,0x0b,0x10,0xef,0x00,0x9e,0x34, + 0x31,0x30,0x39,0x00,0x00,0x00,0x78,0xad, + 0x06,0xff,0x29,0xef,0x8d,0x06,0xff,0x8d, + 0x3f,0xff,0xa2,0x00,0xbd,0xaa,0xaa,0x95, + 0x4b,0xca,0x10,0xf8,0xa2,0x42,0xbd,0x50, + 0x10,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xdd,0xbd,0x91,0x10,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x41, + 0x10,0xce,0x3e,0x10,0x88,0xd0,0xed,0x4c, + 0x17,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x11,0xee,0x03, + 0x02,0xc6,0xf9,0xd0,0x0a,0xa9,0x4b,0x8d, + 0x02,0x02,0xa9,0x00,0x8d,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0xff,0x8d,0xaa, + 0xaa,0xe6,0xfb,0xd0,0x02,0xe6,0xfc,0xca, + 0x60,0xa4,0xf8,0xa2,0x02,0x20,0x3d,0x02, + 0x85,0xf8,0x98,0xa2,0x06,0x20,0x3d,0x02, + 0x20,0xfa,0x00,0xa0,0x00,0x8c,0x91,0x01, + 0x98,0xa2,0x02,0x20,0x3d,0x02,0xc5,0xf8, + 0xd0,0xe9,0x20,0x1f,0x02,0x85,0x2d,0x4a, + 0xd0,0x3f,0x20,0x33,0x02,0x4a,0x90,0x47, + 0x20,0x33,0x02,0x4a,0x90,0xcb,0xc8,0x20, + 0x1f,0x02,0x85,0x2d,0xc9,0x40,0x90,0x0b, + 0xa2,0x02,0x20,0x34,0x02,0x85,0x2d,0x20, + 0x1f,0x02,0xa8,0x20,0x1f,0x02,0xaa,0xbd, + 0xc3,0x01,0xe0,0x10,0x90,0x06,0x8a,0xa2, + 0x04,0x20,0x34,0x02,0xa6,0x2d,0xe8,0x20, + 0xfa,0x00,0xd0,0xfb,0x88,0xd0,0xf8,0xf0, + 0xaa,0x20,0x1f,0x02,0xc9,0x7f,0xf0,0x26, + 0xe9,0x00,0xa2,0x00,0x20,0x3d,0x02,0x85, + 0x2e,0x20,0x31,0x02,0x65,0xfb,0xa6,0x2d, + 0x85,0x2d,0xa5,0xfc,0xe5,0x2e,0x85,0x2e, + 0xe8,0xb1,0x2d,0x18,0x69,0x00,0xc8,0x20, + 0xfa,0x00,0xd0,0xf5,0xf0,0xd1,0xa5,0x2d, + 0xc9,0x02,0xf0,0x09,0x20,0x31,0x02,0x8d, + 0x91,0x01,0x98,0xf0,0xd2,0xa5,0xfb,0x85, + 0x2d,0xa5,0xfc,0x85,0x2e,0xad,0x06,0xff, + 0x09,0x10,0x8d,0x06,0xff,0x8d,0x3e,0xff, + 0xa9,0x00,0x85,0xf9,0x58,0x4c,0xaa,0xaa, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC16WD[] = { + {ftFastDisable, 0x1013 -0x0fff}, + {ftOverlap, 0x101a -0x0fff}, + {ftOverlapLo, 0x101c -0x0fff}, + {ftOverlapHi, 0x101d -0x0fff}, + {ftIBufferSize, 0x1024 -0x0fff}, + {ftReloc, 0x1027 -0x0fff}, + {ftStackSize, 0x102f -0x0fff}, + {ftReloc, 0x1032 -0x0fff}, + {ftSizePages, 0x103a -0x0fff}, + {ftSizeLo, 0x103d -0x0fff}, + {ftSizeHi, 0x103e -0x0fff}, + {ftEndLo, 0x1040 -0x0fff}, + {ftEndHi, 0x1041 -0x0fff}, + {ftReloc, 0x1047 -0x0fff}, + {ftReloc, 0x104a -0x0fff}, + {ftDeCall, 0x104e -0x0fff}, + {ftInposLo, 0x1053 -0x0fff}, + {ftInposHi, 0x1054 -0x0fff}, + {ftMaxGamma, 0x107d -0x0fff}, + {ftEscValue, 0x1093 -0x0fff}, + {ftWrapCount, 0x1094 -0x0fff}, + {ftOutposLo, 0x1096 -0x0fff}, + {ftOutposHi, 0x1097 -0x0fff}, + {ftEscBits, 0x10a3 -0x0fff}, + {ftEsc8Bits, 0x10ab -0x0fff}, + {ftEscBits, 0x10b9 -0x0fff}, + {ft1MaxGamma, 0x10dc -0x0fff}, + {ft8MaxGamma, 0x10e0 -0x0fff}, + {ft2MaxGamma, 0x110c -0x0fff}, + {ftExtraBits, 0x1112 -0x0fff}, + {ftOp, 0x112b -0x0fff}, + {ftFastDisable, 0x1151 -0x0fff}, + {ftCli, 0x115b -0x0fff}, + {ftExecLo, 0x115d -0x0fff}, + {ftExecHi, 0x115e -0x0fff}, + {ftEnd,0} +}; + +static unsigned char headerC16B[] = { + 0x01,0x10,0x0b,0x10,0xef,0x00,0x9e,0x34, + 0x31,0x30,0x39,0x00,0x00,0x00,0x78,0xad, + 0x06,0xff,0x29,0xef,0x8d,0x06,0xff,0x8d, + 0x3f,0xff,0xa2,0x34,0xbd,0x46,0x10,0x9d, + 0xff,0x01,0xca,0xd0,0xf7,0xa2,0xca,0xbd, + 0x79,0x10,0x9d,0xf6,0x00,0xca,0xd0,0xf7, + 0xa0,0xaa,0xca,0xbd,0xaa,0xaa,0x9d,0x00, + 0xff,0x8a,0xd0,0xf6,0xce,0x37,0x10,0xce, + 0x34,0x10,0x88,0xd0,0xed,0x4c,0x16,0x01, + 0x48,0xad,0xaa,0xaa,0x2a,0x85,0xf7,0xee, + 0x02,0x02,0xd0,0x03,0xee,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0x8d,0xaa,0xaa, + 0xe6,0xfa,0xd0,0x02,0xe6,0xfb,0xca,0x60, + 0xa4,0xf8,0xa2,0x02,0x20,0x2f,0x02,0x85, + 0xf8,0x98,0xa2,0x06,0x20,0x2f,0x02,0x20, + 0xf9,0x00,0xa0,0x00,0x98,0xa2,0x02,0x20, + 0x2f,0x02,0xc5,0xf8,0xd0,0xec,0x20,0x11, + 0x02,0x85,0x2d,0x4a,0xd0,0x3f,0x20,0x25, + 0x02,0x4a,0x90,0x47,0x20,0x25,0x02,0x4a, + 0x90,0xce,0xc8,0x20,0x11,0x02,0x85,0x2d, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0x26, + 0x02,0x85,0x2d,0x20,0x11,0x02,0xa8,0x20, + 0x11,0x02,0xaa,0xbd,0xb0,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0x26,0x02, + 0xa6,0x2d,0xe8,0x20,0xf9,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xad,0x20,0x11,0x02, + 0xc9,0x7f,0xf0,0x23,0xe9,0x00,0xa2,0x00, + 0x20,0x2f,0x02,0x85,0x2e,0x20,0x23,0x02, + 0x65,0xfa,0xa6,0x2d,0x85,0x2d,0xa5,0xfb, + 0xe5,0x2e,0x85,0x2e,0xe8,0xb1,0x2d,0xc8, + 0x20,0xf9,0x00,0xd0,0xf8,0xf0,0xd4,0xa5, + 0xfa,0x85,0x2d,0xa5,0xfb,0x85,0x2e,0xad, + 0x06,0xff,0x09,0x10,0x8d,0x06,0xff,0x8d, + 0x3e,0xff,0xa9,0x00,0x85,0xf9,0x58,0x20, + 0xbe,0x8b,0x4c,0xea,0x8b,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC16B[] = { + {ftFastDisable, 0x1013 -0x0fff}, + {ftIBufferSize, 0x101a -0x0fff}, + {ftReloc, 0x101d -0x0fff}, + {ftStackSize, 0x1025 -0x0fff}, + {ftReloc, 0x1028 -0x0fff}, + {ftSizePages, 0x1030 -0x0fff}, + {ftSizeLo, 0x1033 -0x0fff}, + {ftSizeHi, 0x1034 -0x0fff}, + {ftEndLo, 0x1036 -0x0fff}, + {ftEndHi, 0x1037 -0x0fff}, + {ftReloc, 0x103d -0x0fff}, + {ftReloc, 0x1040 -0x0fff}, + {ftDeCall, 0x1044 -0x0fff}, + {ftInposLo, 0x1049 -0x0fff}, + {ftInposHi, 0x104a -0x0fff}, + {ftMaxGamma, 0x1065 -0x0fff}, + {ftEscValue, 0x107b -0x0fff}, + {ftOutposLo, 0x107d -0x0fff}, + {ftOutposHi, 0x107e -0x0fff}, + {ftEscBits, 0x108a -0x0fff}, + {ftEsc8Bits, 0x1092 -0x0fff}, + {ftEscBits, 0x109d -0x0fff}, + {ft1MaxGamma, 0x10c0 -0x0fff}, + {ft8MaxGamma, 0x10c4 -0x0fff}, + {ft2MaxGamma, 0x10f0 -0x0fff}, + {ftExtraBits, 0x10f6 -0x0fff}, + {ftFastDisable, 0x1123 -0x0fff}, + {ftCli, 0x112d -0x0fff}, + {ftEnd,0} +}; + +static unsigned char headerC128[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x34,0xbd,0x40, + 0x1c,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xbf,0xbd,0x73,0x1c,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x31, + 0x1c,0xce,0x2e,0x1c,0x88,0xd0,0xed,0x4c, + 0x16,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x03,0xee,0x03, + 0x02,0x68,0x60,0xe8,0x8a,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x12,0xe8,0xe0, + 0x07,0xd0,0xf2,0xf0,0x0b,0xa2,0x07,0xe8, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x2a, + 0xca,0xd0,0xf5,0x18,0x60,0x80,0x00,0x8d, + 0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6,0xfb, + 0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20,0x2f, + 0x02,0x85,0xf8,0x98,0xa2,0x06,0x20,0x2f, + 0x02,0x20,0xf9,0x00,0xa0,0x00,0x98,0xa2, + 0x02,0x20,0x2f,0x02,0xc5,0xf8,0xd0,0xec, + 0x20,0x11,0x02,0x85,0xa3,0x4a,0xd0,0x3f, + 0x20,0x25,0x02,0x4a,0x90,0x47,0x20,0x25, + 0x02,0x4a,0x90,0xce,0xc8,0x20,0x11,0x02, + 0x85,0xa3,0xc9,0x40,0x90,0x0b,0xa2,0x02, + 0x20,0x26,0x02,0x85,0xa3,0x20,0x11,0x02, + 0xa8,0x20,0x11,0x02,0xaa,0xbd,0xa5,0x01, + 0xe0,0x10,0x90,0x06,0x8a,0xa2,0x04,0x20, + 0x26,0x02,0xa6,0xa3,0xe8,0x20,0xf9,0x00, + 0xd0,0xfb,0x88,0xd0,0xf8,0xf0,0xad,0x20, + 0x11,0x02,0xc9,0x7f,0xf0,0x23,0xe9,0x00, + 0xa2,0x00,0x20,0x2f,0x02,0x85,0xa4,0x20, + 0x23,0x02,0x65,0xfa,0xa6,0xa3,0x85,0xa3, + 0xa5,0xfb,0xe5,0xa4,0x85,0xa4,0xe8,0xb1, + 0xa3,0xc8,0x20,0xf9,0x00,0xd0,0xf8,0xf0, + 0xd4,0xa9,0x00,0x8d,0x00,0xff,0xa5,0xfa, + 0x8d,0x10,0x12,0xa5,0xfb,0x8d,0x11,0x12, + 0x58,0x4c,0xaa,0xaa,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00 +}; + +struct FixEntry fixTableC128[] = { + {ftIBufferSize, 0x1c14 -0x1bff}, + {ftReloc, 0x1c17 -0x1bff}, + {ftStackSize, 0x1c1f -0x1bff}, + {ftReloc, 0x1c22 -0x1bff}, + {ftSizePages, 0x1c2a -0x1bff}, + {ftSizeLo, 0x1c2d -0x1bff}, + {ftSizeHi, 0x1c2e -0x1bff}, + {ftEndLo, 0x1c30 -0x1bff}, + {ftEndHi, 0x1c31 -0x1bff}, + {ftReloc, 0x1c37 -0x1bff}, + {ftReloc, 0x1c3a -0x1bff}, + {ftDeCall, 0x1c3e -0x1bff}, + {ftInposLo, 0x1c43 -0x1bff}, + {ftInposHi, 0x1c44 -0x1bff}, + {ftMaxGamma, 0x1c5f -0x1bff}, + {ftEscValue, 0x1c75 -0x1bff}, + {ftOutposLo, 0x1c77 -0x1bff}, + {ftOutposHi, 0x1c78 -0x1bff}, + {ftEscBits, 0x1c84 -0x1bff}, + {ftEsc8Bits, 0x1c8c -0x1bff}, + {ftEscBits, 0x1c97 -0x1bff}, + {ft1MaxGamma, 0x1cba -0x1bff}, + {ft8MaxGamma, 0x1cbe -0x1bff}, + {ft2MaxGamma, 0x1cea -0x1bff}, + {ftExtraBits, 0x1cf0 -0x1bff}, + {ftCli, 0x1d1f -0x1bff}, + {ftExecLo, 0x1d21 -0x1bff}, + {ftExecHi, 0x1d22 -0x1bff}, + {ftEnd,0} +}; + +static unsigned char headerC128W[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x00,0xbd,0xaa, + 0xaa,0x95,0x4b,0xca,0x10,0xf8,0xa2,0x40, + 0xbd,0x4a,0x1c,0x9d,0xff,0x01,0xca,0xd0, + 0xf7,0xa2,0xbf,0xbd,0x89,0x1c,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x3b,0x1c,0xce,0x38,0x1c,0x88,0xd0, + 0xed,0x4c,0x16,0x01,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0,0x0f, + 0xee,0x03,0x02,0xd0,0x0a,0xa9,0x4b,0x8d, + 0x02,0x02,0xa9,0x00,0x8d,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0x8d,0xaa,0xaa, + 0xe6,0xfa,0xd0,0x02,0xe6,0xfb,0xca,0x60, + 0xa4,0xf8,0xa2,0x02,0x20,0x3b,0x02,0x85, + 0xf8,0x98,0xa2,0x06,0x20,0x3b,0x02,0x20, + 0xf9,0x00,0xa0,0x00,0x98,0xa2,0x02,0x20, + 0x3b,0x02,0xc5,0xf8,0xd0,0xec,0x20,0x1d, + 0x02,0x85,0xa3,0x4a,0xd0,0x3f,0x20,0x31, + 0x02,0x4a,0x90,0x47,0x20,0x31,0x02,0x4a, + 0x90,0xce,0xc8,0x20,0x1d,0x02,0x85,0xa3, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0x32, + 0x02,0x85,0xa3,0x20,0x1d,0x02,0xa8,0x20, + 0x1d,0x02,0xaa,0xbd,0xa5,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0x32,0x02, + 0xa6,0xa3,0xe8,0x20,0xf9,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xad,0x20,0x1d,0x02, + 0xc9,0x7f,0xf0,0x23,0xe9,0x00,0xa2,0x00, + 0x20,0x3b,0x02,0x85,0xa4,0x20,0x2f,0x02, + 0x65,0xfa,0xa6,0xa3,0x85,0xa3,0xa5,0xfb, + 0xe5,0xa4,0x85,0xa4,0xe8,0xb1,0xa3,0xc8, + 0x20,0xf9,0x00,0xd0,0xf8,0xf0,0xd4,0xa9, + 0x00,0x8d,0x00,0xff,0xa5,0xfa,0x8d,0x10, + 0x12,0xa5,0xfb,0x8d,0x11,0x12,0x58,0x4c, + 0xaa,0xaa,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00 +}; + +struct FixEntry fixTableC128W[] = { + {ftOverlap, 0x1c14 -0x1bff}, + {ftOverlapLo, 0x1c16 -0x1bff}, + {ftOverlapHi, 0x1c17 -0x1bff}, + {ftIBufferSize, 0x1c1e -0x1bff}, + {ftReloc, 0x1c21 -0x1bff}, + {ftStackSize, 0x1c29 -0x1bff}, + {ftReloc, 0x1c2c -0x1bff}, + {ftSizePages, 0x1c34 -0x1bff}, + {ftSizeLo, 0x1c37 -0x1bff}, + {ftSizeHi, 0x1c38 -0x1bff}, + {ftEndLo, 0x1c3a -0x1bff}, + {ftEndHi, 0x1c3b -0x1bff}, + {ftReloc, 0x1c41 -0x1bff}, + {ftReloc, 0x1c44 -0x1bff}, + {ftDeCall, 0x1c48 -0x1bff}, + {ftInposLo, 0x1c4d -0x1bff}, + {ftInposHi, 0x1c4e -0x1bff}, + {ftMaxGamma, 0x1c75 -0x1bff}, + {ftEscValue, 0x1c8b -0x1bff}, + {ftOutposLo, 0x1c8d -0x1bff}, + {ftOutposHi, 0x1c8e -0x1bff}, + {ftEscBits, 0x1c9a -0x1bff}, + {ftEsc8Bits, 0x1ca2 -0x1bff}, + {ftEscBits, 0x1cad -0x1bff}, + {ft1MaxGamma, 0x1cd0 -0x1bff}, + {ft8MaxGamma, 0x1cd4 -0x1bff}, + {ft2MaxGamma, 0x1d00 -0x1bff}, + {ftExtraBits, 0x1d06 -0x1bff}, + {ftCli, 0x1d35 -0x1bff}, + {ftExecLo, 0x1d37 -0x1bff}, + {ftExecHi, 0x1d38 -0x1bff}, + {ftEnd,0} +}; + +static unsigned char headerC128D[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x34,0xbd,0x40, + 0x1c,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xd4,0xbd,0x73,0x1c,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x31, + 0x1c,0xce,0x2e,0x1c,0x88,0xd0,0xed,0x4c, + 0x16,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x03,0xee,0x03, + 0x02,0x68,0x60,0xe8,0x8a,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x12,0xe8,0xe0, + 0x07,0xd0,0xf2,0xf0,0x0b,0xa2,0x07,0xe8, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x2a, + 0xca,0xd0,0xf5,0x18,0x60,0x80,0x00,0x8d, + 0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6,0xfb, + 0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20,0x2f, + 0x02,0x85,0xf8,0x98,0xa2,0x06,0x20,0x2f, + 0x02,0x20,0xf9,0x00,0xa0,0x00,0x8c,0x90, + 0x01,0x98,0xa2,0x02,0x20,0x2f,0x02,0xc5, + 0xf8,0xd0,0xe9,0x20,0x11,0x02,0x85,0xa3, + 0x4a,0xd0,0x3f,0x20,0x25,0x02,0x4a,0x90, + 0x47,0x20,0x25,0x02,0x4a,0x90,0xcb,0xc8, + 0x20,0x11,0x02,0x85,0xa3,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x26,0x02,0x85,0xa3, + 0x20,0x11,0x02,0xa8,0x20,0x11,0x02,0xaa, + 0xbd,0xba,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x26,0x02,0xa6,0xa3,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xaa,0x20,0x11,0x02,0xc9,0x7f,0xf0, + 0x26,0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02, + 0x85,0xa4,0x20,0x23,0x02,0x65,0xfa,0xa6, + 0xa3,0x85,0xa3,0xa5,0xfb,0xe5,0xa4,0x85, + 0xa4,0xe8,0xb1,0xa3,0x18,0x69,0x00,0xc8, + 0x20,0xf9,0x00,0xd0,0xf5,0xf0,0xd1,0xa5, + 0xa3,0xc9,0x02,0xf0,0x09,0x20,0x23,0x02, + 0x8d,0x90,0x01,0x98,0xf0,0xd2,0xa9,0x00, + 0x8d,0x00,0xff,0xa5,0xfa,0x8d,0x10,0x12, + 0xa5,0xfb,0x8d,0x11,0x12,0x58,0x4c,0xaa, + 0xaa,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC128D[] = { + {ftIBufferSize, 0x1c14 -0x1bff}, + {ftReloc, 0x1c17 -0x1bff}, + {ftStackSize, 0x1c1f -0x1bff}, + {ftReloc, 0x1c22 -0x1bff}, + {ftSizePages, 0x1c2a -0x1bff}, + {ftSizeLo, 0x1c2d -0x1bff}, + {ftSizeHi, 0x1c2e -0x1bff}, + {ftEndLo, 0x1c30 -0x1bff}, + {ftEndHi, 0x1c31 -0x1bff}, + {ftReloc, 0x1c37 -0x1bff}, + {ftReloc, 0x1c3a -0x1bff}, + {ftDeCall, 0x1c3e -0x1bff}, + {ftInposLo, 0x1c43 -0x1bff}, + {ftInposHi, 0x1c44 -0x1bff}, + {ftMaxGamma, 0x1c5f -0x1bff}, + {ftEscValue, 0x1c75 -0x1bff}, + {ftOutposLo, 0x1c77 -0x1bff}, + {ftOutposHi, 0x1c78 -0x1bff}, + {ftEscBits, 0x1c84 -0x1bff}, + {ftEsc8Bits, 0x1c8c -0x1bff}, + {ftEscBits, 0x1c9a -0x1bff}, + {ft1MaxGamma, 0x1cbd -0x1bff}, + {ft8MaxGamma, 0x1cc1 -0x1bff}, + {ft2MaxGamma, 0x1ced -0x1bff}, + {ftExtraBits, 0x1cf3 -0x1bff}, + {ftOp, 0x1d0c -0x1bff}, + {ftCli, 0x1d34 -0x1bff}, + {ftExecLo, 0x1d36 -0x1bff}, + {ftExecHi, 0x1d37 -0x1bff}, + {ftEnd,0} +}; + +static unsigned char headerC128B[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x34,0xbd,0x40, + 0x1c,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xc8,0xbd,0x73,0x1c,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x31, + 0x1c,0xce,0x2e,0x1c,0x88,0xd0,0xed,0x4c, + 0x16,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x03,0xee,0x03, + 0x02,0x68,0x60,0xe8,0x8a,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x12,0xe8,0xe0, + 0x07,0xd0,0xf2,0xf0,0x0b,0xa2,0x07,0xe8, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x2a, + 0xca,0xd0,0xf5,0x18,0x60,0x80,0x00,0x8d, + 0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6,0xfb, + 0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20,0x2f, + 0x02,0x85,0xf8,0x98,0xa2,0x06,0x20,0x2f, + 0x02,0x20,0xf9,0x00,0xa0,0x00,0x98,0xa2, + 0x02,0x20,0x2f,0x02,0xc5,0xf8,0xd0,0xec, + 0x20,0x11,0x02,0x85,0xa3,0x4a,0xd0,0x3f, + 0x20,0x25,0x02,0x4a,0x90,0x47,0x20,0x25, + 0x02,0x4a,0x90,0xce,0xc8,0x20,0x11,0x02, + 0x85,0xa3,0xc9,0x40,0x90,0x0b,0xa2,0x02, + 0x20,0x26,0x02,0x85,0xa3,0x20,0x11,0x02, + 0xa8,0x20,0x11,0x02,0xaa,0xbd,0xae,0x01, + 0xe0,0x10,0x90,0x06,0x8a,0xa2,0x04,0x20, + 0x26,0x02,0xa6,0xa3,0xe8,0x20,0xf9,0x00, + 0xd0,0xfb,0x88,0xd0,0xf8,0xf0,0xad,0x20, + 0x11,0x02,0xc9,0x7f,0xf0,0x23,0xe9,0x00, + 0xa2,0x00,0x20,0x2f,0x02,0x85,0xa4,0x20, + 0x23,0x02,0x65,0xfa,0xa6,0xa3,0x85,0xa3, + 0xa5,0xfb,0xe5,0xa4,0x85,0xa4,0xe8,0xb1, + 0xa3,0xc8,0x20,0xf9,0x00,0xd0,0xf8,0xf0, + 0xd4,0xa9,0x00,0x8d,0x00,0xff,0xa5,0xfa, + 0x8d,0x10,0x12,0xa5,0xfb,0x8d,0x11,0x12, + 0x58,0x20,0xf3,0x51,0x20,0x81,0x5a,0x20, + 0xf6,0x4a,0x6c,0x02,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +}; + +struct FixEntry fixTableC128B[] = { + {ftIBufferSize, 0x1c14 -0x1bff}, + {ftReloc, 0x1c17 -0x1bff}, + {ftStackSize, 0x1c1f -0x1bff}, + {ftReloc, 0x1c22 -0x1bff}, + {ftSizePages, 0x1c2a -0x1bff}, + {ftSizeLo, 0x1c2d -0x1bff}, + {ftSizeHi, 0x1c2e -0x1bff}, + {ftEndLo, 0x1c30 -0x1bff}, + {ftEndHi, 0x1c31 -0x1bff}, + {ftReloc, 0x1c37 -0x1bff}, + {ftReloc, 0x1c3a -0x1bff}, + {ftDeCall, 0x1c3e -0x1bff}, + {ftInposLo, 0x1c43 -0x1bff}, + {ftInposHi, 0x1c44 -0x1bff}, + {ftMaxGamma, 0x1c5f -0x1bff}, + {ftEscValue, 0x1c75 -0x1bff}, + {ftOutposLo, 0x1c77 -0x1bff}, + {ftOutposHi, 0x1c78 -0x1bff}, + {ftEscBits, 0x1c84 -0x1bff}, + {ftEsc8Bits, 0x1c8c -0x1bff}, + {ftEscBits, 0x1c97 -0x1bff}, + {ft1MaxGamma, 0x1cba -0x1bff}, + {ft8MaxGamma, 0x1cbe -0x1bff}, + {ft2MaxGamma, 0x1cea -0x1bff}, + {ftExtraBits, 0x1cf0 -0x1bff}, + {ftCli, 0x1d1f -0x1bff}, + {ftEnd,0} +}; + +static unsigned char headerC128BD[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x34,0xbd,0x40, + 0x1c,0x9d,0xff,0x01,0xca,0xd0,0xf7,0xa2, + 0xdd,0xbd,0x73,0x1c,0x9d,0xf6,0x00,0xca, + 0xd0,0xf7,0xa0,0xaa,0xca,0xbd,0xaa,0xaa, + 0x9d,0x00,0xff,0x8a,0xd0,0xf6,0xce,0x31, + 0x1c,0xce,0x2e,0x1c,0x88,0xd0,0xed,0x4c, + 0x16,0x01,0x48,0xad,0xaa,0xaa,0x2a,0x85, + 0xf7,0xee,0x02,0x02,0xd0,0x03,0xee,0x03, + 0x02,0x68,0x60,0xe8,0x8a,0x06,0xf7,0xd0, + 0x03,0x20,0x00,0x02,0x90,0x12,0xe8,0xe0, + 0x07,0xd0,0xf2,0xf0,0x0b,0xa2,0x07,0xe8, + 0x06,0xf7,0xd0,0x03,0x20,0x00,0x02,0x2a, + 0xca,0xd0,0xf5,0x18,0x60,0x80,0x00,0x8d, + 0xaa,0xaa,0xe6,0xfa,0xd0,0x02,0xe6,0xfb, + 0xca,0x60,0xa4,0xf8,0xa2,0x02,0x20,0x2f, + 0x02,0x85,0xf8,0x98,0xa2,0x06,0x20,0x2f, + 0x02,0x20,0xf9,0x00,0xa0,0x00,0x8c,0x90, + 0x01,0x98,0xa2,0x02,0x20,0x2f,0x02,0xc5, + 0xf8,0xd0,0xe9,0x20,0x11,0x02,0x85,0xa3, + 0x4a,0xd0,0x3f,0x20,0x25,0x02,0x4a,0x90, + 0x47,0x20,0x25,0x02,0x4a,0x90,0xcb,0xc8, + 0x20,0x11,0x02,0x85,0xa3,0xc9,0x40,0x90, + 0x0b,0xa2,0x02,0x20,0x26,0x02,0x85,0xa3, + 0x20,0x11,0x02,0xa8,0x20,0x11,0x02,0xaa, + 0xbd,0xc3,0x01,0xe0,0x10,0x90,0x06,0x8a, + 0xa2,0x04,0x20,0x26,0x02,0xa6,0xa3,0xe8, + 0x20,0xf9,0x00,0xd0,0xfb,0x88,0xd0,0xf8, + 0xf0,0xaa,0x20,0x11,0x02,0xc9,0x7f,0xf0, + 0x26,0xe9,0x00,0xa2,0x00,0x20,0x2f,0x02, + 0x85,0xa4,0x20,0x23,0x02,0x65,0xfa,0xa6, + 0xa3,0x85,0xa3,0xa5,0xfb,0xe5,0xa4,0x85, + 0xa4,0xe8,0xb1,0xa3,0x18,0x69,0x00,0xc8, + 0x20,0xf9,0x00,0xd0,0xf5,0xf0,0xd1,0xa5, + 0xa3,0xc9,0x02,0xf0,0x09,0x20,0x23,0x02, + 0x8d,0x90,0x01,0x98,0xf0,0xd2,0xa9,0x00, + 0x8d,0x00,0xff,0xa5,0xfa,0x8d,0x10,0x12, + 0xa5,0xfb,0x8d,0x11,0x12,0x58,0x20,0xf3, + 0x51,0x20,0x81,0x5a,0x20,0xf6,0x4a,0x6c, + 0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00 +}; + +struct FixEntry fixTableC128BD[] = { + {ftIBufferSize, 0x1c14 -0x1bff}, + {ftReloc, 0x1c17 -0x1bff}, + {ftStackSize, 0x1c1f -0x1bff}, + {ftReloc, 0x1c22 -0x1bff}, + {ftSizePages, 0x1c2a -0x1bff}, + {ftSizeLo, 0x1c2d -0x1bff}, + {ftSizeHi, 0x1c2e -0x1bff}, + {ftEndLo, 0x1c30 -0x1bff}, + {ftEndHi, 0x1c31 -0x1bff}, + {ftReloc, 0x1c37 -0x1bff}, + {ftReloc, 0x1c3a -0x1bff}, + {ftDeCall, 0x1c3e -0x1bff}, + {ftInposLo, 0x1c43 -0x1bff}, + {ftInposHi, 0x1c44 -0x1bff}, + {ftMaxGamma, 0x1c5f -0x1bff}, + {ftEscValue, 0x1c75 -0x1bff}, + {ftOutposLo, 0x1c77 -0x1bff}, + {ftOutposHi, 0x1c78 -0x1bff}, + {ftEscBits, 0x1c84 -0x1bff}, + {ftEsc8Bits, 0x1c8c -0x1bff}, + {ftEscBits, 0x1c9a -0x1bff}, + {ft1MaxGamma, 0x1cbd -0x1bff}, + {ft8MaxGamma, 0x1cc1 -0x1bff}, + {ft2MaxGamma, 0x1ced -0x1bff}, + {ftExtraBits, 0x1cf3 -0x1bff}, + {ftOp, 0x1d0c -0x1bff}, + {ftCli, 0x1d34 -0x1bff}, + {ftEnd,0} +}; + +static unsigned char headerC128BW[] = { + 0x01,0x1c,0x0b,0x1c,0xef,0x00,0x9e,0x37, + 0x31,0x38,0x31,0x00,0x00,0x00,0x78,0xa9, + 0x3f,0x8d,0x00,0xff,0xa2,0x00,0xbd,0xaa, + 0xaa,0x95,0x4b,0xca,0x10,0xf8,0xa2,0x40, + 0xbd,0x4a,0x1c,0x9d,0xff,0x01,0xca,0xd0, + 0xf7,0xa2,0xc8,0xbd,0x89,0x1c,0x9d,0xf6, + 0x00,0xca,0xd0,0xf7,0xa0,0xaa,0xca,0xbd, + 0xaa,0xaa,0x9d,0x00,0xff,0x8a,0xd0,0xf6, + 0xce,0x3b,0x1c,0xce,0x38,0x1c,0x88,0xd0, + 0xed,0x4c,0x16,0x01,0x48,0xad,0xaa,0xaa, + 0x2a,0x85,0xf7,0xee,0x02,0x02,0xd0,0x0f, + 0xee,0x03,0x02,0xd0,0x0a,0xa9,0x4b,0x8d, + 0x02,0x02,0xa9,0x00,0x8d,0x03,0x02,0x68, + 0x60,0xe8,0x8a,0x06,0xf7,0xd0,0x03,0x20, + 0x00,0x02,0x90,0x12,0xe8,0xe0,0x07,0xd0, + 0xf2,0xf0,0x0b,0xa2,0x07,0xe8,0x06,0xf7, + 0xd0,0x03,0x20,0x00,0x02,0x2a,0xca,0xd0, + 0xf5,0x18,0x60,0x80,0x00,0x8d,0xaa,0xaa, + 0xe6,0xfa,0xd0,0x02,0xe6,0xfb,0xca,0x60, + 0xa4,0xf8,0xa2,0x02,0x20,0x3b,0x02,0x85, + 0xf8,0x98,0xa2,0x06,0x20,0x3b,0x02,0x20, + 0xf9,0x00,0xa0,0x00,0x98,0xa2,0x02,0x20, + 0x3b,0x02,0xc5,0xf8,0xd0,0xec,0x20,0x1d, + 0x02,0x85,0xa3,0x4a,0xd0,0x3f,0x20,0x31, + 0x02,0x4a,0x90,0x47,0x20,0x31,0x02,0x4a, + 0x90,0xce,0xc8,0x20,0x1d,0x02,0x85,0xa3, + 0xc9,0x40,0x90,0x0b,0xa2,0x02,0x20,0x32, + 0x02,0x85,0xa3,0x20,0x1d,0x02,0xa8,0x20, + 0x1d,0x02,0xaa,0xbd,0xae,0x01,0xe0,0x10, + 0x90,0x06,0x8a,0xa2,0x04,0x20,0x32,0x02, + 0xa6,0xa3,0xe8,0x20,0xf9,0x00,0xd0,0xfb, + 0x88,0xd0,0xf8,0xf0,0xad,0x20,0x1d,0x02, + 0xc9,0x7f,0xf0,0x23,0xe9,0x00,0xa2,0x00, + 0x20,0x3b,0x02,0x85,0xa4,0x20,0x2f,0x02, + 0x65,0xfa,0xa6,0xa3,0x85,0xa3,0xa5,0xfb, + 0xe5,0xa4,0x85,0xa4,0xe8,0xb1,0xa3,0xc8, + 0x20,0xf9,0x00,0xd0,0xf8,0xf0,0xd4,0xa9, + 0x00,0x8d,0x00,0xff,0xa5,0xfa,0x8d,0x10, + 0x12,0xa5,0xfb,0x8d,0x11,0x12,0x58,0x20, + 0xf3,0x51,0x20,0x81,0x5a,0x20,0xf6,0x4a, + 0x6c,0x02,0x03,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00 +}; + +struct FixEntry fixTableC128BW[] = { + {ftOverlap, 0x1c14 -0x1bff}, + {ftOverlapLo, 0x1c16 -0x1bff}, + {ftOverlapHi, 0x1c17 -0x1bff}, + {ftIBufferSize, 0x1c1e -0x1bff}, + {ftReloc, 0x1c21 -0x1bff}, + {ftStackSize, 0x1c29 -0x1bff}, + {ftReloc, 0x1c2c -0x1bff}, + {ftSizePages, 0x1c34 -0x1bff}, + {ftSizeLo, 0x1c37 -0x1bff}, + {ftSizeHi, 0x1c38 -0x1bff}, + {ftEndLo, 0x1c3a -0x1bff}, + {ftEndHi, 0x1c3b -0x1bff}, + {ftReloc, 0x1c41 -0x1bff}, + {ftReloc, 0x1c44 -0x1bff}, + {ftDeCall, 0x1c48 -0x1bff}, + {ftInposLo, 0x1c4d -0x1bff}, + {ftInposHi, 0x1c4e -0x1bff}, + {ftMaxGamma, 0x1c75 -0x1bff}, + {ftEscValue, 0x1c8b -0x1bff}, + {ftOutposLo, 0x1c8d -0x1bff}, + {ftOutposHi, 0x1c8e -0x1bff}, + {ftEscBits, 0x1c9a -0x1bff}, + {ftEsc8Bits, 0x1ca2 -0x1bff}, + {ftEscBits, 0x1cad -0x1bff}, + {ft1MaxGamma, 0x1cd0 -0x1bff}, + {ft8MaxGamma, 0x1cd4 -0x1bff}, + {ft2MaxGamma, 0x1d00 -0x1bff}, + {ftExtraBits, 0x1d06 -0x1bff}, + {ftCli, 0x1d35 -0x1bff}, + {ftEnd,0} +}; + +struct FixStruct fixStruct[] = { + {headerC64, sizeof(headerC64), fixTableC64, "C64", FIXF_C64}, + {headerC64S, sizeof(headerC64S), fixTableC64S, "C64 short", FIXF_C64|FIXF_SHORT}, + {headerC64SB, sizeof(headerC64SB), fixTableC64SB, "C64 short basic", FIXF_C64|FIXF_SHORT|FIXF_BASIC}, + {headerC64SW, sizeof(headerC64SW), fixTableC64SW, "C64 short wrap", FIXF_C64|FIXF_SHORT|FIXF_WRAP}, + {headerC64D, sizeof(headerC64D), fixTableC64D, "C64 delta", FIXF_C64|FIXF_DLZ}, + {headerC64F, sizeof(headerC64F), fixTableC64F, "C64 fast", FIXF_C64|FIXF_FAST}, + {headerC64W, sizeof(headerC64W), fixTableC64W, "C64 wrap", FIXF_C64|FIXF_WRAP}, + {headerC64WD, sizeof(headerC64WD), fixTableC64WD, "C64 wrap delta", FIXF_C64|FIXF_WRAP|FIXF_DLZ}, + {headerC64WF, sizeof(headerC64WF), fixTableC64WF, "C64 fast wrap", FIXF_C64|FIXF_WRAP|FIXF_FAST}, + {headerVIC20, sizeof(headerVIC20), fixTableVIC20, "VIC20", FIXF_VIC20}, + {headerVIC20B, sizeof(headerVIC20B), fixTableVIC20B, "VIC20 basic", FIXF_VIC20|FIXF_BASIC}, + {headerVIC20W, sizeof(headerVIC20W), fixTableVIC20W, "VIC20 wrap", FIXF_VIC20|FIXF_WRAP}, + {headerVIC20S, sizeof(headerVIC20S), fixTableVIC20S, "VIC20 short", FIXF_VIC20|FIXF_SHORT}, + {headerVIC20SB, sizeof(headerVIC20SB), fixTableVIC20SB, "VIC20 short basic", FIXF_VIC20|FIXF_SHORT|FIXF_BASIC}, + {headerVIC20SW, sizeof(headerVIC20SW), fixTableVIC20SW, "VIC20 short wrap", FIXF_VIC20|FIXF_SHORT|FIXF_WRAP}, + {headerVIC20WD, sizeof(headerVIC20WD), fixTableVIC20WD, "VIC20 wrap delta", FIXF_VIC20|FIXF_WRAP|FIXF_DLZ}, + {headerVIC20FB, sizeof(headerVIC20FB), fixTableVIC20FB, "VIC20 fast basic", FIXF_VIC20|FIXF_FAST|FIXF_BASIC}, + {headerC16, sizeof(headerC16), fixTableC16, "C16/+4", FIXF_C16}, + {headerC16W, sizeof(headerC16W), fixTableC16W, "C16/+4 wrap", FIXF_C16|FIXF_WRAP}, + {headerC16WD, sizeof(headerC16WD), fixTableC16WD, "C16/+4 wrap delta", FIXF_C16|FIXF_WRAP|FIXF_DLZ}, + {headerC16B, sizeof(headerC16B), fixTableC16B, "C16/+4 basic", FIXF_C16|FIXF_BASIC}, + {headerC128, sizeof(headerC128), fixTableC128, "C128", FIXF_C128}, + {headerC128W, sizeof(headerC128W), fixTableC128W, "C128 wrap", FIXF_C128|FIXF_WRAP}, + {headerC128D, sizeof(headerC128D), fixTableC128D, "C128 delta", FIXF_C128|FIXF_DLZ}, + {headerC128B, sizeof(headerC128B), fixTableC128B, "C128 basic", FIXF_C128|FIXF_BASIC}, + {headerC128BD, sizeof(headerC128BD), fixTableC128BD, "C128 basic delta", FIXF_C128|FIXF_BASIC|FIXF_DLZ}, + {headerC128BW, sizeof(headerC128BW), fixTableC128BW, "C128 basic wrap", FIXF_C128|FIXF_BASIC|FIXF_WRAP}, + {NULL, 0, NULL, NULL, 0} +}; diff --git a/loader/tools/pucrunch/sa_uncrunch.asm b/loader/tools/pucrunch/sa_uncrunch.asm new file mode 100644 index 0000000..f977841 --- /dev/null +++ b/loader/tools/pucrunch/sa_uncrunch.asm @@ -0,0 +1,291 @@ +; SHORT+IRQLOAD 354 bytes +; no rle =~ -83 bytes -> 271 +; fixed params =~ -48 bytes -> 306 +; 223 bytes + +SHORT = 0 ;1 ; assume file is ok +IRQLOAD = 0 ;1 + + processor 6502 + + +LZPOS EQU $9e ; 2 ZeroPage temporaries +bitstr EQU $fb ; 1 temporary (does not need to be ZP) + + + ORG $c000 + + ; Call with X = HI of packed data, Y = LO of packed data + ; Returns exec address in X = HI and Y = LO + ; Carry will be set for error, cleared for OK +#if SHORT == 0 + sei + lda #$35 + sta 1 +#endif +#if IRQLOAD + ;jsr initloader +#else + ; Setup read pointer + sty INPOS + stx INPOS+1 +#endif + +#if SHORT + ldx #5 +222$ jsr getbyt ; skip 'p', 'u', endAddr HI&LO, leave starting escape in A + dex + bne 222$ +#else + jsr getbyt ; 'p' + cmp #112 + beq 9$ + sec ; error + jmp eof2 +9$ jsr getbyt ; 'u' + cmp #117 + beq 8$ + sec ; error + jmp eof2 +8$ jsr getbyt ; skip endAddr + jsr getbyt + jsr getbyt +#endif + sta esc+1 ; starting escape + + jsr getbyt ; read startAddr + sta OUTPOS + jsr getbyt + sta OUTPOS+1 + jsr getbyt ; read # of escape bits + sta escB0+1 + sta escB1+1 + lda #8 + sec + sbc escB1+1 + sta noesc+1 ; 8-escBits + + jsr getbyt + sta mg+1 ; maxGamma + 1 + lda #9 + sec + sbc mg+1 ; 8 - maxGamma == (8 + 1) - (maxGamma + 1) + sta longrle+1 + jsr getbyt + sta mg1+1 ; (1< LZ77 + ;tya ; A = 0 + jsr get1bit ; X = 0 + lsr ; bit -> C, A = 0 + bcc lz77_2 ; A=0 -> LZPOS+1 + ;***FALL THRU*** + + ; e..e01 + jsr get1bit ; X = 0 + lsr ; bit -> C, A = 0 + bcc newesc ; e..e010 + ;***FALL THRU*** + + ; e..e011 +srle iny ; Y is 1 bigger than MSB loops + jsr getval ; Y is 1, get len, X = 0 + sta LZPOS ; xstore - Save length LSB +mg1 cmp #64 ; ** PARAMETER 63-64 -> C clear, 64-64 -> C set.. + bcc chrcode ; short RLE, get bytecode + +longrle ldx #2 ; ** PARAMETER 111111xxxxxx + jsr getbits ; get 3/2/1 more bits to get a full byte, X = 0 + sta LZPOS ; xstore - Save length LSB + + jsr getval ; length MSB, X = 0 + tay ; Y is 1 bigger than MSB loops + +chrcode jsr getval ; Byte Code, X = 0 + tax ; this is executed most of the time anyway + lda table-1,x ; Saves one jump if done here (loses one txa) + + cpx #16 ; 32 ; 31-32 -> C clear, 32-32 -> C set.. + bcc 1$ ; 1..31, we got the right byte from the table + + ; Ranks 32..64 (11111°xxxxx), get byte.. + txa ; get back the value (5 valid bits) + ldx #4 ;3 + jsr getbits ; get 3 more bits to get a full byte, X = 0 + +1$ ldx LZPOS ; xstore - get length LSB + inx ; adjust for cpx#$ff;bne -> bne +dorle jsr putch + dex + bne dorle ; xstore 0..255 -> 1..256 + dey + bne dorle ; Y was 1 bigger than wanted originally +mainbeq beq main ; reverse condition -> jump always + + +lz77 jsr getval ; X = 0 +mg21 cmp #127 ; ** PARAMETER Clears carry (is maximum value) + bne noeof + ; EOF +eof +#if SHORT == 0 + ; EOF +eof clc +eof2 lda #$37 + sta 1 + cli +#endif +#if IRQLOAD + ;jsr endloader +lo = .+1 +hi = .+2 + jmp $aaaa +#else +hi ldx #0 +lo ldy #0 + rts +#endif + + +noeof sbc #0 ; C is clear -> subtract 1 (1..126 -> 0..125) +elzpb ldx #0 ; ** PARAMETER (more bits to get) + jsr getchkf ; clears Carry, X = 0 + +lz77_2 sta LZPOS+1 ; offset MSB + jsr getbyte ; clears Carry, X = 0 + ; Note: Already eor:ed in the compressor.. + ;eor #255 ; offset LSB 2's complement -1 (i.e. -X = ~X+1) + adc OUTPOS ; -offset -1 + curpos (C is clear) + ldx LZPOS ; xstore = LZLEN (read before it's overwritten) + sta LZPOS + + lda OUTPOS+1 + sbc LZPOS+1 ; takes C into account + sta LZPOS+1 ; copy X+1 number of chars from LZPOS to OUTPOS + ;ldy #0 ; Y was 0 originally, we don't change it + + inx ; adjust for cpx#$ff;bne -> bne +lzloop lda (LZPOS),y ; using abs,y is 3 bytes longer, only 1 cycle/byte faster + jsr putch ; Note: must be copied forwards! + iny ; Y does not wrap because X=0..255 and Y initially 0 + dex + bne lzloop ; X loops, (256,1..255) + beq mainbeq ; jump through another beq (-1 byte, +3 cycles) + + +getnew pha ; 1 Byte/3 cycles + +#if IRQLOAD + ;jsr READBYTE ; should not change X or Y, return byte in A + ; An implementation would first check if any bytes are available + ; and if not, transfer a block from drive. +#else +INPOS = *+1 + lda $aaaa ; ** PARAMETER + inc INPOS + bne 0$ + inc INPOS+1 +#endif +0$ sec + rol ; Shift out the next bit and + ; shift in C=1 (last bit marker) + sta bitstr ; bitstr initial value = $80 == empty + pla ; 1 Byte/4 cycles + rts + ; 25+12 = 37 + +; getval : Gets a 'static huffman coded' value +; ** Scratches X, returns the value in A ** +getval inx ; X <- 1 + txa ; set the top bit (value is 1..255) +gv0 asl bitstr + bne 1$ + jsr getnew +1$ bcc getchk ; got 0-bit + inx +mg cpx #7 ; ** PARAMETER unary code maximum length + 1 + bne gv0 + beq getchk ; inverse condition -> jump always + ; getval: 18 bytes + ; 15 + 17*n + 6+15*n+12 + 36*n/8 = 33 + 32*n + 36*n/8 cycles + +; getbits: Gets X bits from the stream +; ** Scratches X, returns the value in A ** +getbyte ldx #7 +get1bit inx ;2 +getbits asl bitstr + bne 1$ + jsr getnew +1$ rol ;2 +getchk dex ;2 more bits to get ? +getchkf bne getbits ;2/3 + clc ;2 return carry cleared + rts ;6+6 + + +OUTPOS = *+1 ; ZP +putch sta $aaaa ; ** parameter + inc OUTPOS ; ZP + bne 0$ + inc OUTPOS+1 ; ZP +0$ rts + + + +table dc.b 0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + diff --git a/loader/tools/pucrunch/smakefile b/loader/tools/pucrunch/smakefile new file mode 100644 index 0000000..0c56d29 --- /dev/null +++ b/loader/tools/pucrunch/smakefile @@ -0,0 +1,6 @@ +all: pucrunch + +pucrunch: pucrunch.c pucrunch.h + sc link ansi warn=all ignore=61 pucrunch.c nostkchk data=far parms=register lib lib:scm.lib opt opttime optsched optpeep optinlocal + #debug=line + # disasm=pucrunch.asm diff --git a/loader/tools/pucrunch/uncrunch-z80.asm b/loader/tools/pucrunch/uncrunch-z80.asm new file mode 100644 index 0000000..ef657a0 --- /dev/null +++ b/loader/tools/pucrunch/uncrunch-z80.asm @@ -0,0 +1,456 @@ + +;* +;* +;* PUCRUNCH unpacker for GB +;* Modeled after Pasi Ojala's C64 code. +;* +;* Written in RGBDS +;* +;* V1.0 - Ported to GB by Jeff Frohwein, started 22-Jul-99 +;* V1.1 - Various optimizations, 23-Jul-99 +;* V1.2 - Even more optimizations, 23-Jul-99 +;* V1.3 - Fixed a bug in the code. 256 byte copy didn't work. 24-Feb-00 +;* +;* Note: If you unpack to VRAM than the screen needs to be +;* turned off because no checks for VRAM available are made. + +; Pucrunch file format + +;;; db INPOS low (endAddr + overlap - size) & 0xff +;;; db INPOS high (endAddr + overlap - size) >> 8 +;;; db 'p' +;;; db 'u' +;;; db (endAddr - 0x100) & 0xff +;;; db (endAddr - 0x100) >> 8 +; db escape>>(8-escBits) +;;; db (start & 0xff) (OUTPOS low) +;;; db (start >> 8) (OUTPOS high) +; db escBits +; db maxGamma + 1 +; db (1<> 8) +; db rleUsed (31) ;needed +; ds rleUsed +; ....data.... + + PUSHS + + SECTION "Pucrunch Vars",BSS + +;bitstr DB +escPu DB +;InPtr DW +OutPtr DW +;regx DB +;regy DB +lzpos DW +EscBits DB +Esc8Bits DB +MaxGamma DB +Max1Gamma DB +Max2Gamma DB +Max8Gamma DB +ExtraBits DB +tablePu DS 31 + + + SECTION "Pucrunch High Vars",HRAM + +regy DB + + POPS + +; HL = InPtr +; D = bitstr +; E = X +; BC = temps + +; ****** Unpack pucrunch data ****** +; Entry: HL = Source packed data +; DE = Destination for unpacked data + +Unpack: + ld16r OutPtr,de + +; Read the file header & setup variables + + ld bc,6 + add hl,bc + + ld a,[hl+] + ld [escPu],a + + inc hl + inc hl + + ld a,[hl+] + ld [EscBits],a + ld b,a + + ld a,8 + sub b + ld [Esc8Bits],a + + ld a,[hl+] + ld [MaxGamma],a + dec a + ld b,a + ld a,8 + sub b + ld [Max8Gamma],a + + ld a,[hl+] + ld [Max1Gamma],a + add a + dec a + ld [Max2Gamma],a + + ld a,[hl+] + ld [ExtraBits],a + + inc hl + inc hl + + ld a,[hl+] + ld b,a + + ld de,tablePu + +; Copy the RLE table (maximum of 31 bytes) to RAM + + inc b + srl b + jr nc,.orleloop + +.rleloop: + ld a,[hl+] + ld [de],a + inc de + +.orleloop: + ld a,[hl+] + ld [de],a + inc de + + dec b + jr nz,.rleloop + + + + ld d,$80 + + jr .main + + +.newesc: + ld b,a + + ld a,[escPu] + ld [regy],a + + ld a,[EscBits] + ld e,a + + ld a,b + + inc e + + call .getchk + + ld [escPu],a + + ld a,[regy] + + ; Fall through and get the rest of the bits. + +.noesc: + ld b,a + + ld a,[Esc8Bits] + ld e,a + + ld a,b + + inc e + + call .getchk + +; Write out the escaped/normal byte + + ld16 bc,OutPtr + ld [bc],a + inc bc + ld16r OutPtr,bc + + ; Fall through and check the escape bits again + +.main: + ld a,[EscBits] + ld e,a + + xor a ; A = 0 + ld [regy],a + + inc e + + call .getchk ; X=2 -> X=0 + + ld b,a + ld a,[escPu] + cp b + ld a,b + + jr nz,.noesc ; Not the escape code -> get the rest of the byte + + ; Fall through to packed code + + call .getval ; X=0 -> X=0 + + ld [lzpos],a ; xstore - save the length for a later time + + srl a ; cmp #1 ; LEN == 2 ? (A is never 0) + jp nz,.lz77 ; LEN != 2 -> LZ77 + + call .get1bit ; X=0 -> X=0 + + srl a ; bit -> C, A = 0 + + jp nc,.lz77_2 ; A=0 -> LZPOS+1 LZ77, len=2 + + ; e..e01 + call .get1bit ; X=0 -> X=0 + srl a ; bit -> C, A = 0 + jp nc,.newesc ; e..e010 New Escape + + ; e..e011 Short/Long RLE + ld a,[regy] ; Y is 1 bigger than MSB loops + inc a + ld [regy],a + + call .getval ; Y is 1, get len, X=0 -> X=0 + ld [lzpos],a ; xstore - Save length LSB + + ld c,a + + ld a,[Max1Gamma] + ld b,a + + ld a,c + + cp b ; ** PARAMETER 63-64 -> C set, 64-64 -> C clear.. + + jr c,.chrcode ; short RLE, get bytecode + + ; Otherwise it's long RLE +.longrle: + ld b,a + ld a,[Max8Gamma] + ld e,a ; ** PARAMETER 111111xxxxxx + ld a,b + + call .getbits ; get 3/2/1 more bits to get a full byte, X=2 -> X=0 + ld [lzpos],a ; xstore - Save length LSB + + call .getval ; length MSB, X=0 -> X=0 + + ld [regy],a ; Y is 1 bigger than MSB loops + +.chrcode: + call .getval ; Byte Code, X=0 -> X=0 + + ld e,a + + ld c,(tablePu-1)%256 + add c + ld c,a + ld a,(tablePu-1)/256 + adc 0 + ld b,a + + ld a,e + cp 32 ; 31-32 -> C set, 32-32 -> C clear.. + ld a,[bc] + jr c,.less32 ; 1..31 + + ; Not ranks 1..31, -> 11111°xxxxx (32..64), get byte.. + + ld a,e ; get back the value (5 valid bits) + + ld e,3 + + call .getbits ; get 3 more bits to get a full byte, X=3 -> X=0 + +.less32: + push hl + push af + + ld a,[lzpos] + ld e,a ; xstore - get length LSB + + ld b,e + inc b ; adjust for cpx#$ff;bne -> bne + + ld a,[regy] + ld c,a + + ld16r hl,OutPtr + + pop af + +.dorle: + ld [hl+],a + + dec b + jr nz,.dorle ; xstore 0..255 -> 1..256 + + dec c + jr nz,.dorle ; Y was 1 bigger than wanted originally + + ld16r OutPtr,hl + + pop hl + jp .main + +.lz77: + call .getval ; X=0 -> X=0 + + ld b,a + + ld a,[Max2Gamma] + cp b ; end of file ? + ret z ; yes, exit + + ld a,[ExtraBits] ; ** PARAMETER (more bits to get) + ld e,a + + ld a,b + + dec a ; subtract 1 (1..126 -> 0..125) + + inc e + + call .getchk ;f ; clears Carry, X=0 -> X=0 + +.lz77_2: + ld [lzpos+1],a ; offset MSB + + ld e,8 + + call .getbits ; clears Carry, X=8 -> X=0 + + ; Note: Already eor:ed in the compressor.. + ld b,a + + ld a,[lzpos] + ld e,a ; xstore - LZLEN (read before it's overwritten) + + ld a,[OutPtr] + add b ; -offset -1 + curpos (C is clear) + ld [lzpos],a + + ld a,[lzpos+1] + ld b,a + + ld a,[OutPtr+1] + ccf + sbc b + ld [lzpos+1],a ; copy X+1 number of chars from LZPOS to OUTPOS + + inc e ; adjust for cpx#$ff;bne -> bne + +; Write decompressed bytes out to RAM + ld b,e + + push de + push hl + + ld16r hl,lzpos + ld16r de,OutPtr + + ld a,b + or a ; Is it zero? + jr z,.zero ; yes + + inc b + srl b + jr nc,.olzloop + +.lzloop: + ld a,[hl+] ; Note: Must be copied forward + ld [de],a + inc de +.olzloop: + ld a,[hl+] ; Note: Must be copied forward + ld [de],a + inc de + + dec b + jr nz,.lzloop ; X loops, (256,1..255) + + ld16r OutPtr,de + + pop hl + pop de + jp .main + +.zero: + ld b,128 + jr .lzloop + +; getval : Gets a 'static huffman coded' value +; ** Scratches X, returns the value in A ** +.getval: ; X must be 0 when called! + ld a,1 + ld e,a +.loop0: + sla d + + jr nz,.loop1 + + ld d,[hl] + inc hl + + rl d ; Shift in C=1 (last bit marker) + ; bitstr initial value = $80 == empty +.loop1: + jr nc,.getchk ; got 0-bit + + inc e + + ld b,a ; save a + + ld a,[MaxGamma] + cp e + + ld a,b ; restore a + + jr nz,.loop0 + + jr .getchk + + +; getbits: Gets X bits from the stream +; ** Scratches X, returns the value in A ** + +.get1bit: + inc e +.getbits: + sla d + + jr nz,.loop3 + + ld d,[hl] + inc hl + + rl d ; Shift in C=1 (last bit marker) + ; bitstr initial value = $80 == empty +.loop3: + rla +.getchk: + dec e + + jr nz,.getbits + or a ; clear carry flag + ret \ No newline at end of file diff --git a/loader/tools/pucrunch/uncrunch.asm b/loader/tools/pucrunch/uncrunch.asm new file mode 100644 index 0000000..a225f82 --- /dev/null +++ b/loader/tools/pucrunch/uncrunch.asm @@ -0,0 +1,798 @@ +;$a533 relink lines +;$a871 run +; if Z=1, calls a659: a68e [settextptr], ffe7 [clall], varptrs, +; a81d [restore], stack set +;$a7ae 42926 newstt BASIC Warm Start + + +;DLZ = 1 ; Delta LZ77 -- add offset +;DLZ = 0 + +;FAST = 1 ; A little faster, but a little longer +;FAST = -1 ; Slower, but shorter +;WRAP = 1 ; wrap buffer enabled + +C64 = 0 +VIC20 = 1 +C16 = 2 +C128 = 3 + +#if MACH = C64 +#if FAST = 0 +#else +#if FAST = 1 +#else +#if FAST = -1 +#else + err invalid speed option.. +#endif ; -1 +#endif ; 1 +#endif ; 0 +#else +#if MACH = VIC20 +;#if FAST = 0 +;#else +; err invalid speed option.. +;#endif ; 0 +#else +#if MACH = C16 +#if FAST = 0 +#else + err invalid speed option.. +#endif ; 0 +#else +#if MACH = C128 +#else + err invalid machine +#endif ; C128 +#endif ; C16 +#endif ; VIC20 +#endif ; C64 + + +#if FAST = -1 && (MACH = C64 || MACH = VIC20) + +;#if MACH = VIC20 +;#if WRAP +; err -fshort and wrap not possible with VIC20 +;#endif +;#endif + +#if DLZ = 1 + err DLZ not valid in -fshort +#endif + processor 6502 + ; Note: Possibilities for shortening + ; - No basic end address set: 8 bytes + ; - No 2 MHz mode set/reset: 6 bytes + ; - No settable memory config: 2 bytes + ; - No basic line: 12 bytes + ; - No RLE-code: 12 bytes + max 31 table bytes + ; - No RLE at all: 50 bytes + max 31 table bytes +#if MACH = C128 +BASEND EQU $1210 ; start of basic variables +LZPOS EQU $a3 +#else +BASEND EQU $2d ; start of basic variables +LZPOS EQU $2f +#endif + +bitstr EQU $f7 ; Hint the value beforehand +#if WRAP +#if (MACH = VIC20 || MACH = C16) +wrap EQU $f9 ; Hint the value beforehand +#endif +WRAPBUF EQU $004b ; 'wrap' buffer, 22 bytes ($02a7 for 89 bytes) +#endif + +#if MACH = C64 + ORG $0801 + DC.B $0b,8,$ef,0 ; '239 SYS2061' + DC.B $9e,$32,$30,$36 + DC.B $31,0,0,0 +#endif +#if MACH = VIC20 + ORG $1201 + DC.B $0b,$12,$ef,0 ; '239 SYS4621' + DC.B $9e,$34,$36,$32 + DC.B $31,0,0,0 +#endif + +#if MACH = C64 + sei + lda #$38 + sta 1 +#endif + lda #$aa ;+1fTBEndLo ** Set the basic prg end address + sta BASEND + lda #$aa ;+1ftBEndHi ** + sta BASEND+1 + +#if WRAP + ldx #0 ;+1ftOverlap ** parameter - # of overlap bytes-1 off $ffff +overlap lda $aaaa,x ;+1ftOverlapLo+2ftOverlapHi ** parameter start of off-end bytes + sta WRAPBUF,x + dex + bpl overlap +#if MACH != C64 + txs ; use stack from $ff downwards +#endif +#endif + + ldx #block_stack_end-block_stack+1 ;+1ftStackSize +packlp2 lda block_stack-1,x ;+2ftReloc + dc.b $9d ; sta $nnnn,x + dc.w block_stack_-1 ; (ZP addressing only addresses ZP!) + dex + bne packlp2 + + ; Max 255 extra bytes are copied, but they are at the start + ; of the data, i.e. the decompressor. + + ; Note that we would overwrite this copy loop in extreme cases + ; if we didn't move the data further up in memory to prevent it. + + ldy #$aa ;+1ftSizePages ** parameter SIZE high + 1 +cploop dex ; ldx #$ff on the first round + lda $aaaa,x ;+1ftSizeLo+2ftSizeHi ** parameter DATAEND-0x100 + sta $ff00,x ;+1ftEndLo+2ftEndHi ** parameter ORIG LEN-0x100+ reserved bytes + txa ;cpx #0 + bne cploop + dec cploop+6 ;+2ftReloc + dec cploop+3 ;+2ftReloc + dey + bne cploop + jmp main ;+0ftDeCall + + + +block_stack +#rorg $f7 ; $f7 - ~$1e0 +block_stack_ + +bitstr dc.b $80 ; ZP $80 == Empty +esc dc.b $00 ;+0ftEscValue ** parameter (saves a byte when here) +#if MACH = VIC20 || MACH = C16 +#if WRAP +wrap dc.b $ff ;+0ftWrapCount ** parameter - 'memory size' in pages + N +#endif +#endif + +OUTPOS = *+1 ; ZP +putch sta $aaaa ;+1ftOutposLo+2ftOutposHi ** parameter + inc OUTPOS ; ZP + bne 0$ + inc OUTPOS+1 ; ZP +0$ dex + rts + +newesc ldy esc ; remember the old code (top bits for escaped byte) + ldx #2 ;+1ftEscBits ** PARAMETER + jsr getchkf ; get & save the new escape code (allows X=0) + sta esc + tya ; pre-set the bits + ; Fall through and get the rest of the bits. +noesc ldx #6 ;+1ftEsc8Bits ** PARAMETER + jsr getchkf + jsr putch ; output the escaped/normal byte + ; Fall through and check the escape bits again +main ldy #0 ; Reset to a defined state + tya ; A = 0 + ldx #2 ;+1ftEscBits ** PARAMETER number of escape bits (allows X=0) + jsr getchkf ; X=2 -> X=0 + cmp esc + bne noesc ; Not the escape code -> get the rest of the byte + ; Fall through to packed code + + jsr getval ; X=0 -> X=0 + sta LZPOS ; xstore - save the length for a later time + lsr ; cmp #1 ; LEN == 2 ? (A is never 0) + bne lz77 ; LEN != 2 -> LZ77 + ;tya ; A = 0 + + jsr getbit ; X=0 -> X=0 + bcc lz77_2 ; A=0 -> LZPOS+1 LZ77, len=2 + ; e..e01 + jsr getbit ; X=0 -> X=0 + bcc newesc ; e..e010 New Escape + + ; e..e011 Short/Long RLE + iny ; Y is 1 bigger than MSB loops + jsr getval ; Y is 1, get len, X=0 -> X=0 + sta LZPOS ; xstore - Save length LSB + cmp #64 ;+1ft1MaxGamma ** PARAMETER 63-64 -> C clear, 64-64 -> C set.. + bcc chrcode ; short RLE, get bytecode + ; Otherwise it's long RLE +longrle ldx #2 ;+1ft8MaxGamma ** PARAMETER 111111xxxxxx + jsr getbits ; get 3/2/1 more bits to get a full byte, X=2 -> X=0 + sta LZPOS ; xstore - Save length LSB + + jsr getval ; length MSB, X=0 -> X=0 + tay ; Y is 1 bigger than MSB loops + +chrcode jsr getval ; Byte Code, X=0 -> X=0 + tax ; this is executed most of the time anyway + lda table-1,x ; Saves one jump if done here (loses one txa) + + cpx #16 ;32 ; 31-32 -> C clear, 32-32 -> C set.. + bcc 1$ ; 1..31 + + ; Not ranks 1..31, -> 11111°xxxxx (32..64), get byte.. + txa ; get back the value (5 valid bits) + ldx #4 ;3 + jsr getbits ; get 3 more bits to get a full byte, X=3 -> X=0 + +1$ ldx LZPOS ; xstore - get length LSB + inx ; adjust for cpx#$ff;bne -> bne +dorle jsr putch ;+dex + bne dorle ; xstore 0..255 -> 1..256 + dey + bne dorle ; Y was 1 bigger than wanted originally +mainbeq beq main ; reverse condition -> jump always + + +lz77 jsr getval ; X=0 -> X=0 + cmp #127 ;+1ft2MaxGamma ** PARAMETER Clears carry (is maximum value) + beq eof ; EOF + + sbc #0 ; C is clear -> subtract 1 (1..126 -> 0..125) + ldx #0 ;+1ftExtraBits ** PARAMETER (more bits to get) + jsr getchkf ; clears Carry, X=0 -> X=0 + +lz77_2 sta LZPOS+1 ; offset MSB + ldx #8 + jsr getbits ; clears Carry, X=8 -> X=0 + ; Note: Already eor:ed in the compressor.. + ;eor #255 ; offset LSB 2's complement -1 (i.e. -X = ~X+1) + adc OUTPOS ; -offset -1 + curpos (C is clear) + ldx LZPOS ; xstore - LZLEN (read before it's overwritten) + + sta LZPOS + lda OUTPOS+1 + sbc LZPOS+1 ; takes C into account + sta LZPOS+1 ; copy X+1 number of chars from LZPOS to OUTPOS + ;ldy #0 ; Y was 0 originally, we don't change it + inx ; adjust for cpx#$ff;bne -> bne +lzloop lda (LZPOS),y ; Note: *Must* be copied forwards! + iny ; Y does not wrap because X=0..255 and Y initially 0 + jsr putch ;+dex + bne lzloop ; X loops, (256,1..255) + beq mainbeq ; jump through another beq (-1 byte, +3 cycles) + + ; EOF +eof +#if MACH = C64 + lda #$37 ;+1ftMemConfig ** PARAMETER + sta 1 + cli ;+0ftCli ** PARAMETER +#endif +#if BASIC == 1 +#if MACH == C64 + ;jsr $a533 ; relink lines + lda #0 ; set Z + jsr $a871 ; run + jmp $a7ae ; basic warm start +#endif +#if MACH == VIC20 + ;jsr $c533 ; relink lines + lda #0 ; set Z + jsr $c871 ; run + jmp $c7ae ; basic warm start +#endif +#else + jmp $aaaa ;+1ftExecLo+2ftExecHi ** PARAMETER +#endif + + +getbit asl bitstr + bne gbend + pha ; 1 Byte/3 cycles +INPOS = *+1 + lda $aaaa ;+1ftInposLo+2ftInposHi ** parameter + rol ; Shift in C=1 (last bit marker) + sta bitstr ; bitstr initial value = $80 == empty + + inc INPOS ; Does not change C! + bne 0$ + inc INPOS+1 ; Does not change C! +#if WRAP +#if MACH = C64 + bne 0$ +#else + dec wrap + bne 0$ +#endif + ; This code does not change C! + lda #WRAPBUF ; Wrap from $ffff->$0000 -> WRAPBUF + sta INPOS +#if MACH = C64 + ;lda #>WRAPBUF + ;sta INPOS+1 +#else + lda #0 + sta INPOS+1 +#endif +#endif +0$ pla ; 1 Byte/4 cycles +gbend rts + + +; getval : Gets a 'static huffman coded' value +; Scratches X, returns the value in A ** +getval inx ; X must be 0 when called! + txa ; set the top bit (value is 1..255) +0$ jsr getbit + bcc getchk ; got 0-bit + inx + cpx #7 ;+1ftMaxGamma ** parameter + bne 0$ + beq getchk ; inverse condition -> jump always + +getbits jsr getbit + rol ;2 +getchk dex ;2 more bits to get ? +getchkf bne getbits ;2/3 + clc ;2 return carry cleared + rts ;6+6 + + +table dc.b 0,0,0,0,0,0,0 ; the table must be at the end of the file! + dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + +#rend +block_stack_end + + +; ************************************************************************* + +#else + processor 6502 + ; Note: Possibilities for shortening + ; - No basic end address set: 8 bytes + ; - No 2 MHz mode set/reset: 6 bytes + ; - Complete stack overwrite: ~10 bytes + ; - No settable memory config: 2 bytes + ; - No basic line: 12 bytes + ; - No RLE-code: 12 bytes + max 31 table bytes + +#if MACH = C128 +BASEND EQU $1210 ; start of basic variables (updated at EOF) +LZPOS EQU $a3 +#else +BASEND EQU $2d ; start of basic variables (updated at EOF) +LZPOS EQU $2d ; temporary, BASEND *MUST* *BE* *UPDATED* at EOF +#endif + +bitstr EQU $f7 ; Hint the value beforehand +#if MACH = VIC20 || MACH = C16 +#if WRAP +wrap EQU $f9 ; Hint the value beforehand +#endif +#endif + + +#if WRAP +WRAPBUF EQU $004b ; 'wrap' buffer, 22 bytes ($02a7 for 89 bytes) +#endif + +#if MACH = C64 + ORG $0801 + DC.B $0b,8,$ef,0 ; '239 SYS2061' + DC.B $9e,$32,$30,$36 + DC.B $31,0,0,0 +#endif +#if MACH = VIC20 + ORG $1201 + DC.B $0b,$12,$ef,0 ; '239 SYS4621' + DC.B $9e,$34,$36,$32 + DC.B $31,0,0,0 +#endif +#if MACH = C16 + ORG $1001 + DC.B $0b,$10,$ef,0 ; '239 SYS4109' + DC.B $9e,$34,$31,$30 + DC.B $39,0,0,0 +#endif +#if MACH = C128 + ORG $1c01 + DC.B $0b,$1c,$ef,0 ; '239 SYS7181' + DC.B $9e,$37,$31,$38 + DC.B $31,0,0,0 +#endif + + sei +#if MACH = C64 + inc $d030 ;+0ftFastDisable ** or "bit $d030" if 2MHz mode is not enabled + lda #$38 + sta 1 +#endif +#if MACH = C16 + ; clearing $ff06 bit 4, clear $f9 at end + lda $ff06 + and #$ef + sta $ff06 ;+0ftFastDisable ** blank the screen + sta $ff3f ;switch off the ROMs +#endif +#if MACH = C128 + lda #$3f + sta $ff00 +#endif + + +#if WRAP + ldx #0 ;+1ftOverlap ** parameter - # of overlap bytes-1 off $ffff +overlap lda $aaaa,x ;+1ftOverlapLo+2ftOverlapHi ** parameter start of off-end bytes + sta WRAPBUF,x + dex + bpl overlap +#endif + + ldx #block200_end-block200+1 ;+1ftIBufferSize ; $58 ($59 max) +packlp lda block200-1,x ;+2ftReloc + sta block200_-1,x + dex + bne packlp + + ldx #block_stack_end-block_stack+1 ;+1ftStackSize ; $b3 (stack! ~$e8 max) +packlp2 lda block_stack-1,x ;+2ftReloc + dc.b $9d ; sta $nnnn,x + dc.w block_stack_-1 ; (ZP addressing only addresses ZP!) + dex + bne packlp2 + + ldy #$aa ;+1ftSizePages ** parameter SIZE high + 1 (max 255 extra bytes) +cploop dex ; ldx #$ff on the first round + lda $aaaa,x ;+1ftSizeLo+2ftSizeHi ** parameter DATAEND-0x100 + sta $ff00,x ;+1ftEndLo+2ftEndHi ** parameter ORIG LEN-0x100+ reserved bytes + txa ;cpx #0 + bne cploop + dec cploop+6 ;+2ftReloc + dec cploop+3 ;+2ftReloc + dey + bne cploop + jmp main ;+0ftDeCall + + + +block200 +#rorg $200 ; $200-$258 +block200_ + +getnew pha ; 1 Byte/3 cycles +INPOS = *+1 + lda $aaaa ;+1ftInposLo+2ftInposHi ** parameter + rol ; Shift in C=1 (last bit marker) + sta bitstr ; bitstr initial value = $80 == empty + + inc INPOS ; Does not change C! + bne 0$ + inc INPOS+1 ; Does not change C! +#if WRAP +#if MACH = C64 || MACH = C128 ; Wrap at end of mem.. + bne 0$ +#else + dec wrap ; Does not change C! + bne 0$ +#endif + ; This code does not change C! + lda #WRAPBUF ; Wrap from $ffff->$0000 -> WRAPBUF + sta INPOS +#if MACH = C64 + ;lda #>WRAPBUF + ;sta INPOS+1 +#else + lda #0 + sta INPOS+1 +#endif +#endif +0$ pla ; 1 Byte/4 cycles + rts + + +; getval : Gets a 'static huffman coded' value +; Scratches X, returns the value in A ** +getval inx ; X must be 0 when called! + txa ; set the top bit (value is 1..255) +0$ asl bitstr ; Note: a space/time tradeoff + bne 1$ ; the subroutine is only called 1/8 + jsr getnew +1$ bcc getchk ; got 0-bit + inx + cpx #7 ;+1ftMaxGamma ** parameter + bne 0$ + beq getchk ; inverse condition -> jump always + + +; getbits: Gets X bits from the stream +; Scratches X, returns the value in A ** +get8bit ldx #7 +get1bit inx ;2 +getbits asl bitstr ; Note: a space/time tradeoff + bne 1$ ; the subroutine is only called 1/8 + jsr getnew +1$ rol ;2 +getchk dex ;2 more bits to get ? +getchkf bne getbits ;2/3 + clc ;2 return carry cleared + rts ;6+6 + +#rend +block200_end + + + +block_stack +#rorg $f7 ; $f7 - ~$1e0 +block_stack_ + +bitstr dc.b $80 ; ZP $80 == Empty +esc dc.b $00 ;+0ftEscValue ** parameter (saves a byte when here) +#if MACH = VIC20 || MACH = C16 +#if WRAP +wrap dc.b $ff ;+0ftWrapCount ** parameter - 'memory size' in pages + N +#endif +#endif + +OUTPOS = *+1 ; ZP +putch sta $aaaa ;+1ftOutposLo+2ftOutposHi ** parameter + inc OUTPOS ; ZP +#if FAST = 1 ; 8/6 bytes, saves 254 cycles per 256 output bytes (max 0.066s) + beq 0$ + dex + rts +0$ inc OUTPOS+1 + dex + rts +#else + bne 0$ + inc OUTPOS+1 ; ZP +0$ dex + rts +#endif + +newesc ldy esc ; remember the old code (top bits for escaped byte) + ldx #2 ;+1ftEscBits ** PARAMETER + jsr getchkf ; get & save the new escape code (allows X=0) + sta esc + tya ; pre-set the bits + ; Fall through and get the rest of the bits. +noesc ldx #6 ;+1ftEsc8Bits ** PARAMETER + jsr getchkf + jsr putch ; output the escaped/normal byte + ; Fall through and check the escape bits again +main ldy #0 ; Reset to a defined state +#if DLZ = 1 + sty addi+1 ;3 +#endif + tya ; A = 0 + ldx #2 ;+1ftEscBits ** PARAMETER number of escape bits (allows X=0) + jsr getchkf ; X=2 -> X=0 + cmp esc + bne noesc ; Not the escape code -> get the rest of the byte + ; Fall through to packed code + + jsr getval ; X=0 -> X=0 + sta LZPOS ; xstore - save the length for a later time + lsr ; cmp #1 ; LEN == 2 ? (A is never 0) + bne lz77 ; LEN != 2 -> LZ77 + ;tya ; A = 0 +#if FAST = 1 ; 18/12 bytes, ~24 cycles per match (0.7s w/ all 2-byte matches) + asl bitstr + bne 1$ + jsr getnew +1$ bcc lz77_2 + asl bitstr + bne 2$ + jsr getnew +2$ bcc newesc +#else + jsr get1bit ; X=0 -> X=0 + lsr ; bit -> C, A = 0 + bcc lz77_2 ; A=0 -> LZPOS+1 LZ77, len=2 + ; e..e01 + jsr get1bit ; X=0 -> X=0 + lsr ; bit -> C, A = 0 + bcc newesc ; e..e010 New Escape +#endif + + ; e..e011 Short/Long RLE + iny ; Y is 1 bigger than MSB loops + jsr getval ; Y is 1, get len, X=0 -> X=0 + sta LZPOS ; xstore - Save length LSB + cmp #64 ;+1ft1MaxGamma ** PARAMETER 63-64 -> C clear, 64-64 -> C set.. + bcc chrcode ; short RLE, get bytecode + ; Otherwise it's long RLE +longrle ldx #2 ;+1ft8MaxGamma ** PARAMETER 111111xxxxxx + jsr getbits ; get 3/2/1 more bits to get a full byte, X=2 -> X=0 + sta LZPOS ; xstore - Save length LSB + + jsr getval ; length MSB, X=0 -> X=0 + tay ; Y is 1 bigger than MSB loops + +chrcode jsr getval ; Byte Code, X=0 -> X=0 + tax ; this is executed most of the time anyway + lda table-1,x ; Saves one jump if done here (loses one txa) + + cpx #16 ;32 ; 31-32 -> C clear, 32-32 -> C set.. + bcc 1$ ; 1..31 + + ; Not ranks 1..31, -> 11111°xxxxx (32..64), get byte.. + txa ; get back the value (5 valid bits) + ldx #4 ;3 + jsr getbits ; get 3 more bits to get a full byte, X=3 -> X=0 + +1$ ldx LZPOS ; xstore - get length LSB + inx ; adjust for cpx#$ff;bne -> bne +dorle jsr putch ;+dex + bne dorle ; xstore 0..255 -> 1..256 + dey + bne dorle ; Y was 1 bigger than wanted originally +mainbeq beq main ; reverse condition -> jump always + + +lz77 jsr getval ; X=0 -> X=0 + cmp #127 ;+1ft2MaxGamma ** PARAMETER Clears carry (is maximum value) + beq eof ; EOF + + sbc #0 ; C is clear -> subtract 1 (1..126 -> 0..125) + ldx #0 ;+1ftExtraBits ** PARAMETER (more bits to get) + jsr getchkf ; clears Carry, X=0 -> X=0 + +lz77_2 sta LZPOS+1 ; offset MSB + jsr get8bit ; clears Carry, X=8 -> X=0 + ; Note: Already eor:ed in the compressor.. + + ;eor #255 ; offset LSB 2's complement -1 (i.e. -X = ~X+1) + adc OUTPOS ; -offset -1 + curpos (C is clear) + ldx LZPOS ; xstore - LZLEN (read before it's overwritten) + +#if FAST = 1 ; 34/20 + sta lzloop+1 + lda OUTPOS+1 + sbc LZPOS+1 ; takes C into account + sta lzloop+2 ; copy X+1 number of chars from LZPOS to OUTPOS + ;ldy #0 ; Y was 0 originally, we don't change it + + inx ; adjust for cpx#$ff;bne -> bne +lzloop lda $aaaa,y ; Note: *Must* be copied forwards! +#if DLZ = 1 + clc ;1 +addi adc #0 ;2+0ftOp +#endif + sta (OUTPOS),y + iny ; Y does not wrap because X=0..255 and Y initially 0 + dex + bne lzloop ; X loops, (256,1..255) + dey ; 1 2 + tya ; 1 2 + sec ; 1 2 + adc OUTPOS+0 ; 2 3 + sta OUTPOS+0 ; 2 3 + bcc 0$ ; 2 2/3 + inc OUTPOS+1 ; 2 5 +0$ jmp main ; 3 3 +#else + sta LZPOS + lda OUTPOS+1 + sbc LZPOS+1 ; takes C into account + sta LZPOS+1 ; copy X+1 number of chars from LZPOS to OUTPOS + ;ldy #0 ; Y was 0 originally, we don't change it + + inx ; adjust for cpx#$ff;bne -> bne +lzloop lda (LZPOS),y ; Note: *Must* be copied forwards! + ; Note: lda $nnnn,y would be 3 bytes longer, but only 1 cycle faster +#if DLZ = 1 + clc ;1 +addi adc #0 ;2+0ftOp +#endif + iny ; Y does not wrap because X=0..255 and Y initially 0 + jsr putch ;+dex + ; Note: sta (OUTPOS),y would be quite a bit faster (-17), + ; but OUTPOS update would make the code longer (+11 bytes) + bne lzloop ; X loops, (256,1..255) + beq mainbeq ; jump through another beq (-1 byte, +3 cycles) +#endif + + ; EOF +eof +; Adding DLZ makes the routines 22 bytes longer +#if DLZ = 1 + lda LZPOS ;2 LZLEN + cmp #2 ;2 + beq 0$ ;2 Really EOF + jsr get8bit ;3 get ADD + sta addi+1 ;3 + tya ;1 + beq lz77_2 ;2=22 +0$ +#endif + +#if MACH = C64 + lda #$37 ;+1ftMemConfig ** PARAMETER + sta 1 + dec $d030 ;+0ftFastDisable ** or "bit $d030" if 2MHz mode is not enabled +#endif +#if MACH = C128 + lda #$00 + sta $ff00 +#endif + lda OUTPOS ; Set the basic prg end address + sta BASEND + lda OUTPOS+1 + sta BASEND+1 +#if MACH = C16 + ; setting $ff06 bit 4, clear $f9 at end + lda $ff06 + ora #$10 + sta $ff06 ;+0ftFastDisable ** reveal the screen + sta $ff3e ; ** could be a PARAMETER (switch on ROM/RAM) + lda #0 + sta $f9 +#endif + cli ;+0ftCli ** PARAMETER +#if BASIC == 1 +#if MACH == C64 + ;(0302) = a483 + ;jsr $a533 ; relink lines +#if 0 + lda #0 ; set Z + jsr $a871 ; run + direct mode->program mode +#else + jsr $a659 +#endif + jmp $a7ae ; basic warm start +#endif +#if MACH == VIC20 + ;(0302) = c483 + ;jsr $c533 ; relink lines +#if 0 + lda #0 ; set Z + jsr $c871 ; run +#else + jsr $c659 +#endif + jmp $c7ae ; basic warm start +#endif +#if MACH == C128 + jsr $51f3 + jsr $5a81 + jsr $4af6 + jmp ($0302) ;(0302) = 4dc6 +;51F3 +; $5254 ; Back Up Text Pointer +; Perform [clr] +; $927B ; Call 'clall' +; Pointer: Limit-of-memory (bank 1) +; Pointer: Start-of-variables (bank 1) +; Pudef Characters +; $5AE1; Pointer: Start-of-BASIC (bank 0) +; Clear Stack & Work Area +;5a81 ;auto line number increment, collision mask, auto-insert mode flag.. +;4af6 ?? +;4dc6 basic warm start jmp ($0302) +#endif +#if MACH == C16 + ;8a98 perform [clr] + ; z=1 ffe7 -> (032a) -> ef08 CLALL + ; set basic pointers etc. + jsr $8bbe ;perform run+clr + jmp $8bea ;;$8bd6 ;jmp ($0302) ;(0302) = 8712 +#endif +#else + jmp $aaaa ;+1ftExecLo+2ftExecHi ** PARAMETER +#endif + + +table dc.b 0,0,0,0,0,0,0 ; the table must be at the end of the file! + dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + ;dc.b 0,0,0,0,0,0,0,0 + +#rend +block_stack_end + + +#endif + diff --git a/loader/tools/subsizer-0.7pre1/LICENSE.txt b/loader/tools/subsizer-0.7pre1/LICENSE.txt new file mode 100644 index 0000000..1bd30da --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/LICENSE.txt @@ -0,0 +1,30 @@ +subsizer - CBM packer/cruncher + +Copyright (c) 2012, 2015, 2016, 2017 Daniel Kahlin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the names of the copyright holders nor the names of their + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +4. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/loader/tools/subsizer-0.7pre1/README.txt b/loader/tools/subsizer-0.7pre1/README.txt new file mode 100644 index 0000000..76d08e2 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/README.txt @@ -0,0 +1,240 @@ +README.txt +---------- +subsizer 0.7pre1 by Daniel Kahlin (aka tlr), 2017-11-08 + +subsizer: CBM packer/cruncher + +FEATURES + + - fast depack + - good compression + - automatic folding allows full mem operation ($0000-$ffff) + +Compression is slightly worse than for example exomizer but generally better +than pucrunch. Decrunch speed is fairly fast. + + +USAGE + +raw: + # subsizer -r [-o] +mem: + # subsizer -m [-o] +sfx: + # subsizer -x [-X[jmp=][,mem=][,sei]] [-o] + +Loading: + normal: [,,[][,]] + override: ,[,[][,]] + raw: @[,[][,]] + + +EXAMPLES + +$ subsizer -otest.prg file1.prg file2.prg,0x1000 +$ subsizer -otest.prg file1.prg,,0x20 file2.prg +$ subsizer file.prg -Xjmp=0x0801,sei + + +SFX DECRUNCHER + + The self extracting binary options are set with the -X flag. + + header options: (default: normal basic header) + load= - start address of decruncher (implies sysless) + sysless - do not begin with a sys line. + + starting options: + mem= - $01 value after decrunch (default: mem=0x37) + sei - exit with interrupts disabled. + (default: interrupts enabled) + jmp= - jump address after decrunch (default: find sys) + + decruncher options: (default: dirty if start address is below $0400) + clean - "clean" decruncher. A little slower but avoids most + of zp and other system areas. Can crunch $0400-$ffff. + dirty - "dirty" decruncher. Much faster but costs a few + bytes more due to inlining. Will clobber most of zp + + stack. Can crunch $0200-$ffff. + + folding options: (default: automatically fold if required) + fold= - fold data below into the RLE areas higher up + before crunching. This allows $0000-$ffff operation + for any decruncher. + nofold - do not fold. + + +COMPARISON + +The statistics for executable decrunchers below are on two of the files +originally selected as test corpus by j0x for his unreleased cruncher. + +source file: "zorrounpacked.prg" 54105 (214 blks) +(Remember's crack of Zorro (Data East) including intro. Not (pre-)compressed.) + + sorted on size: + crunch sfx + cruncher size (blks) left gain time mem time + -------------------------------------------------------------- + alz: 30095 (119) 55.62% 44.38% 0.97s 1.32M 76.04s + exo209: 30690 (121) 56.72% 43.28% 1.64s 34.28M 9.19s + sbsz06: 30713 (121) 56.77% 43.23% 2.16s 36.27M 7.59s + sbsz06-d: 30724 (121) 56.79% 43.21% 2.13s 37.05M 5.68s + sbsz05: 30756 (122) 56.85% 43.15% 1.97s 37.44M 7.32s + sbsz05-d: 30777 (122) 56.88% 43.12% 1.95s 37.29M 5.58s + lzmpi: 32101 (127) 59.33% 40.67% 1.50s 12.75M 14.71s + pucrn-d: 32135 (127) 59.39% 40.61% 0.07s 2.01M 10.16s + pucrn-s: 32195 (127) 59.50% 40.50% 0.01s 1.75M 12.65s + pucrn: 32219 (127) 59.55% 40.45% 0.01s 1.75M 9.94s + pucrn-f: 32242 (127) 59.59% 40.41% 0.01s 1.75M 9.17s + bitnax06: 33060 (131) 61.10% 38.90% 0.03s 1.11M 4.33s + doynam11: 33154 (131) 61.28% 38.72% 0.05s 1.08M 4.46s + bb20: 33301 (132) 61.55% 38.45% 0.02s 2.60M 7.27s + bb11: 35227 (139) 65.11% 34.89% 0.15s 0.71M 9.82s + -------------------------------------------------------------- + + sorted on sfx time: + crunch sfx + cruncher size (blks) left gain time mem time + -------------------------------------------------------------- + bitnax06: 33060 (131) 61.10% 38.90% 0.03s 1.11M 4.33s + doynam11: 33154 (131) 61.28% 38.72% 0.05s 1.08M 4.46s + sbsz05-d: 30777 (122) 56.88% 43.12% 1.95s 37.29M 5.58s + sbsz06-d: 30724 (121) 56.79% 43.21% 2.13s 37.05M 5.68s + bb20: 33301 (132) 61.55% 38.45% 0.02s 2.60M 7.27s + sbsz05: 30756 (122) 56.85% 43.15% 1.97s 37.44M 7.32s + sbsz06: 30713 (121) 56.77% 43.23% 2.16s 36.27M 7.59s + pucrn-f: 32242 (127) 59.59% 40.41% 0.01s 1.75M 9.17s + exo209: 30690 (121) 56.72% 43.28% 1.64s 34.28M 9.19s + bb11: 35227 (139) 65.11% 34.89% 0.15s 0.71M 9.82s + pucrn: 32219 (127) 59.55% 40.45% 0.01s 1.75M 9.94s + pucrn-d: 32135 (127) 59.39% 40.61% 0.07s 2.01M 10.16s + pucrn-s: 32195 (127) 59.50% 40.50% 0.01s 1.75M 12.65s + lzmpi: 32101 (127) 59.33% 40.67% 1.50s 12.75M 14.71s + alz: 30095 (119) 55.62% 44.38% 0.97s 1.32M 76.04s + -------------------------------------------------------------- + + +source file: "tunnel.prg" 59394 (234 blks) +(Demopart containing lots of unrolled loops.) + + sorted on size: + crunch sfx + cruncher size (blks) left gain time mem time + -------------------------------------------------------------- + alz: 19384 (77) 32.64% 67.36% 0.71s 1.34M 63.32s + sbsz06: 22098 (88) 37.21% 62.79% 2.42s 70.79M 6.08s + sbsz06-d: 22109 (88) 37.22% 62.78% 2.41s 72.32M 4.48s + exo209: 22161 (88) 37.31% 62.69% 3.72s 31.59M 7.74s + sbsz05: 22192 (88) 37.36% 62.64% 1.68s 71.47M 5.95s + sbsz05-d: 22213 (88) 37.40% 62.60% 1.68s 70.36M 4.48s + lzmpi: 23104 (91) 38.90% 61.10% 1.25s 11.14M 11.58s + pucrn-d: 24487 (97) 41.23% 58.77% 0.08s 2.03M 8.65s + pucrn-s: 24834 (98) 41.81% 58.19% 0.01s 2.01M 10.42s + pucrn: 24858 (98) 41.85% 58.15% 0.02s 2.01M 8.50s + pucrn-f: 24881 (98) 41.89% 58.11% 0.02s 2.01M 7.59s + bitnax06: 25156 (100) 42.35% 57.65% 0.24s 1.16M 4.00s + doynam11: 25160 (100) 42.36% 57.64% 0.34s 1.13M 4.12s + bb20: 25244 (100) 42.50% 57.50% 0.03s 2.61M 6.60s + bb11: 26782 (106) 45.09% 54.91% 0.12s 0.71M 8.97s + -------------------------------------------------------------- + + sorted on sfx time: + crunch sfx + cruncher size (blks) left gain time mem time + -------------------------------------------------------------- + bitnax06: 25156 (100) 42.35% 57.65% 0.24s 1.16M 4.00s + doynam11: 25160 (100) 42.36% 57.64% 0.34s 1.13M 4.12s + sbsz05-d: 22213 (88) 37.40% 62.60% 1.68s 70.36M 4.48s + sbsz06-d: 22109 (88) 37.22% 62.78% 2.41s 72.32M 4.48s + sbsz05: 22192 (88) 37.36% 62.64% 1.68s 71.47M 5.95s + sbsz06: 22098 (88) 37.21% 62.79% 2.42s 70.79M 6.08s + bb20: 25244 (100) 42.50% 57.50% 0.03s 2.61M 6.60s + pucrn-f: 24881 (98) 41.89% 58.11% 0.02s 2.01M 7.59s + exo209: 22161 (88) 37.31% 62.69% 3.72s 31.59M 7.74s + pucrn: 24858 (98) 41.85% 58.15% 0.02s 2.01M 8.50s + pucrn-d: 24487 (97) 41.23% 58.77% 0.08s 2.03M 8.65s + bb11: 26782 (106) 45.09% 54.91% 0.12s 0.71M 8.97s + pucrn-s: 24834 (98) 41.81% 58.19% 0.01s 2.01M 10.42s + lzmpi: 23104 (91) 38.90% 61.10% 1.25s 11.14M 11.58s + alz: 19384 (77) 32.64% 67.36% 0.71s 1.34M 63.32s + -------------------------------------------------------------- + + +STANDALONE + +The statistics for the stand alone decruncher below is run on the +"Pearls for Pigs" benchmark as selected by WVL together with his LZWVL +compressor. A simple get byte from memory in reverse routine is used and +included in the timing figures. + + duration outspd inspd + file size (blks) left cycles frms k/s cy/b cy/b + ------------------------------------------------------------------ + pfp1.bin: 2961 (12) 26.90% 724217 36.8 14.6 65.8 244.6 + pfp2.bin: 2201 (9) 44.26% 414274 21.1 11.5 83.3 188.2 + pfp3.bin: 1786 (8) 45.23% 308675 15.7 12.3 78.2 172.8 + pfp4.bin: 3438 (14) 49.00% 613097 31.2 11.0 87.4 178.3 + pfp5.bin: 19631 (78) 56.48% 3497850 178.0 9.6 100.6 178.2 + pfp6.bin: 8407 (34) 26.60% 1803984 91.8 16.9 57.1 214.6 + pfp7.bin: 8768 (35) 43.00% 1661112 84.5 11.8 81.5 189.5 + pfp8.bin: 3086 (13) 54.02% 500954 25.5 11.0 87.7 162.3 + pfp9.bin: 5313 (21) 59.30% 934362 47.5 9.2 104.3 175.9 + ------------------------------------------------------------------ + + +INSPIRATION + - the neverending rants of Charles Bloom + - Exomizer / Magnus Lind + - Pucrunch / Pasi Ojala + - doynax lz + bitnax / Doynax & Bitbreaker + - ByteBoiler / Oneway + - Cruelcrunch / Galleon + - Bitimploder / PET + - Time Cruncher / Matcham + - Cardcruncher / 1001 + ... and many more! + + +HISTORY + +subsizer 0.7pre1, 2017-11-08 + - files crunched with -m will get a load address corresponding to the + least overlap required. + - added -f switch to allow -m crunch in the forward direction. + - updated more generic sys line + - saved a 2 bytes in the stand alone decruncher. + - cleaned up makefiles and added automatic dependency generation. + Thanks to frantic for reporting OSX/clang build issues. + +subsizer 0.6, 2017-04-21 + - improved first pass cost model + - cleaned up verbose output a bit + - saved 10 bytes in the dirty sfx decruncher + - added stand alone decruncher source + +subsizer 0.5.1, 2017-03-21 + - added LICENSE.txt + - give error if no end marker can be found. + - got rid of a few warnings as reported by soci. + - fixed issue with uninitialized memory as reported by compyx. + - added decruncher source due to popular demand. well, only bitbreaker. :) + (caution: encoding may change between versions) + +subsizer 0.5, 2017-03-18 + - implemented working folding, full mem crunching possible + - working loadback and sysless + - implemented memory crunch + - BUG: corrected problem with $0200 decrunch and a large safeuncr together + with the dirty decruncher. + +subsizer 0.4, 2015-11-20 + - added dirty decruncher ($0200-$ffff) + - improved decrunch speed a lot. + +subsizer 0.3, 2015-09-14 + - first somewhat complete version + + + +eof diff --git a/loader/tools/subsizer-0.7pre1/src/Makefile b/loader/tools/subsizer-0.7pre1/src/Makefile new file mode 100644 index 0000000..75754b4 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/Makefile @@ -0,0 +1,42 @@ +# Makefile for subsizer + +# configuration +CPPFLAGS += -MMD -MP +CFLAGS= -O3 -march=native -Wall + +# top level targets +all: subdirs subsizer + +# source files +SRC = subsizer.c \ + params.c match.c pathfinder.c universal.c bits-base.c \ + crunch_normal.c \ + buffer.c memory.c \ + histogram.c \ + bitfunc.c \ + message.c \ + utils.c \ + global.c + +# targets +subsizer: $(SRC:%.c=%.o) sfx/sfx.o + $(CC) $(CFLAGS) -o $@ $^ -lm + cp $@ .. + +# clean +clean: subdirs + rm -f *~ \#*\# + rm -f *.o + rm -f *.d + rm -f a.out + rm -f subsizer + +# handle dependencies +-include $(SRC:%.c=%.d) + +# handle sub directories +export CC LD CPPFLAGS CFLAGS +subdirs: + $(MAKE) -C sfx $(MAKECMDGOALS) + +# eof diff --git a/loader/tools/subsizer-0.7pre1/src/bitfunc.c b/loader/tools/subsizer-0.7pre1/src/bitfunc.c new file mode 100644 index 0000000..5c1f9d7 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/bitfunc.c @@ -0,0 +1,211 @@ +/************************************************************************** + * + * FILE bitfunc.c + * Copyright (c) 2009-2011, 2013-2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * bitstream read/write functions. + * + ******/ +#include +#include + +#include "bitfunc.h" + + +/************************************************************************** + * + * SECTION bit writer + * + ******/ +void bitwr_init(BitWriteState *bws, uint8_t *destbuf, unsigned flags) +{ + bws->ptr = destbuf; + bws->pos = 0; + bws->bits = 0; + + bws->bit = 0; + bws->buf = 0; + bws->bpos = 0; + + bws->flags = flags; + + if (bws->flags & BITMODE_PRESHIFT) { + /* + * first byte shall be rol:ed one bit with a 1 inserted + * we reserve this bit here and apply the shift afterwards + */ + bitwr_write(bws, 0, 1); + } +} + + +void bitwr_write(BitWriteState *bws, uint32_t data, int n) +{ + bws->bits += n; + + /* mask source data */ + data &= (1 << n) - 1; + + /* process bits */ + while (n) { + int fr = 8-bws->bit; + int nn = (n > fr) ? fr : n; + if (bws->bit == 0) { + bws->bpos = bws->pos; + bws->pos++; + } + bws->buf <<= nn; + bws->buf |= data >> (n-nn); + bws->bit += nn; + if (bws->bit == 8) { + bws->ptr[bws->bpos] = bws->buf; + bws->bit = 0; + } + n -= nn; + } +} + + +void bitwr_write8s(BitWriteState *bws, uint8_t data) +{ + if ( !(bws->flags & BITMODE_SIDEBYTE) ) { + bitwr_write(bws, data, 8); + return; + } + + bws->bits += 8; + + bws->ptr[bws->pos] = data; + bws->pos++; +} + + +int bitwr_flush(BitWriteState *bws) +{ + if (bws->bit > 0) { + bitwr_write(bws, 0, 8-bws->bit); + } + + if (bws->flags & BITMODE_PRESHIFT) { + /* + * first byte shall be rol:ed one bit with a 1 inserted + * apply the shift here (MSB was unused since before) + */ + bws->ptr[0] = (bws->ptr[0] << 1) | 0x01; + } + + return bws->pos; +} + + +/************************************************************************** + * + * SECTION bit reader + * + ******/ +void bitrd_init(BitReadState *brs, uint8_t *srcbuf, unsigned int flags) +{ + brs->ptr = srcbuf; + brs->pos = 0; + brs->bits = 0; + + brs->bit = 0; + brs->buf = 0; + + brs->flags = flags; + + if (brs->flags & BITMODE_PRESHIFT) { + /* + * first byte shall be ror:ed one bit with a 1 inserted + * and one bit pop:ed. Preserve buf[0] for later. + */ + uint8_t tmp = brs->ptr[0]; + brs->ptr[0] >>= 1; + bitrd_read(brs, 1); + brs->ptr[0] = tmp; + } +} + + +uint32_t bitrd_read(BitReadState *brs, int n) +{ + brs->bits += n; + uint32_t data = 0; + + while (n) { + if (brs->bit == 0) { + brs->buf = brs->ptr[brs->pos]; + brs->pos++; + brs->bit = 8; + } + int nn = (n > brs->bit) ? brs->bit : n; + + data <<= nn; + data |= brs->buf >> (8-nn); + brs->buf <<= nn; + brs->bit -= nn; + n -= nn; + } + return data; +} + + +uint8_t bitrd_read8s(BitReadState *brs) +{ + if ( !(brs->flags & BITMODE_SIDEBYTE) ) { + return bitrd_read(brs, 8); + } + brs->bits += 8; + uint8_t data = 0; + + data = brs->ptr[brs->pos]; + brs->pos++; + return data; +} + + +#if 0 +/************************************************************************** + * + * SECTION test functions + * + ******/ +void bit_test(void) +{ + int i; + BitWriteState bws; + BitReadState brs; + uint8_t buf[256]; + int len; + bitwr_init(&bws, buf, 0); + + bitwr_write(&bws, 1, 1); + bitwr_write(&bws, 0, 1); + bitwr_write(&bws, 3, 2); + bitwr_write(&bws, 0x5a6b7c, 24); + + len=bitwr_flush(&bws); + printf("buf (%d): ",bws.bits); + for (i=0; i + * Written by Daniel Kahlin + * + * DESCRIPTION + * bitstream read/write functions. + * + ******/ +#ifndef BITFUNC_H +#define BITFUNC_H + +#include + +#define BITMODE_SIDEBYTE (1<<0) +#define BITMODE_PRESHIFT (1<<1) +//#define BITMODE_ROR (1<<2) + +/* bit writer */ +typedef struct { + uint8_t *ptr; + int pos; + int bits; + uint8_t buf; + int bit; + int bpos; + unsigned int flags; +} BitWriteState; + +void bitwr_init(BitWriteState *bws, uint8_t *destbuf, unsigned int flags); +void bitwr_write(BitWriteState *bws, uint32_t data, int n); +void bitwr_write8s(BitWriteState *bws, uint8_t data); +int bitwr_flush(BitWriteState *bws); + +/* bit reader */ +typedef struct { + uint8_t *ptr; + int pos; + int bits; + uint8_t buf; + int bit; + unsigned int flags; +} BitReadState; + +void bitrd_init(BitReadState *brs, uint8_t *srcbuf, unsigned int flags); +uint32_t bitrd_read(BitReadState *brs, int n); +uint8_t bitrd_read8s(BitReadState *brs); + +#endif /* BITFUNC_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/bits-base.c b/loader/tools/subsizer-0.7pre1/src/bits-base.c new file mode 100644 index 0000000..08ad0f7 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/bits-base.c @@ -0,0 +1,390 @@ +/************************************************************************** + * + * FILE bits-base.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * bits-base optimization + * + ******/ +#include +#include +#include + +#include "bitfunc.h" +#include "bits-base.h" +#include "global.h" +#include "histogram.h" +#include "universal.h" + +#define DEBUG_COMPLEXITY 0 +#define HAVE_CACHE 1 + +#define MAX_VALUES 0x10000 + +static unsigned int base_cost[MAX_PARTS]; +static unsigned int so_far[MAX_VALUES+1]; +static unsigned int cost_left[MAX_VALUES+1]; + +#if DEBUG_COMPLEXITY +static size_t n_entry, n_tested, n_copy, n_hits; +#endif + +#if HAVE_CACHE +/************************************************************************** + * + * SECTION optimization cache + * + ******/ +#define CACHE_ENTRIES MAX_VALUES + +typedef struct { + int len; + char parts[MAX_PARTS]; +} CacheEntry; + +static CacheEntry cache[CACHE_ENTRIES][MAX_PARTS]; + +static void invalidate_cache(void) +{ + int id, bit; + for (id = 0; id < CACHE_ENTRIES; id++) { + for (bit = 0; bit < MAX_PARTS; bit++) { + cache[id][bit].len = -1; + } + } +} + +static inline int hash_func(int base) +{ + return base; +} + + +static inline void add_cache(int n_b, int bit, int base, char *enc, int len) +{ + int i; + int id = hash_func(base); + + cache[id][bit].len = len; + for (i = bit; i < n_b; i++) { + cache[id][bit].parts[i] = enc[i]; + } +} + + +static inline int find_cache(int n_b, int bit, int base, char *enc) +{ + int i; + int id = hash_func(base); + + int l = cache[id][bit].len; + if (l >= 0) { + for (i = bit; i < n_b; i++) { + enc[i] = cache[id][bit].parts[i]; + } + } + return l; +} + + +#endif + + +/************************************************************************** + * + * SECTION optimize encoding + * + ******/ +static int calc_enc(int n_b, int bit, int base, char *enc) +{ + int i, min_len = 0x10000000; + char min_enc[MAX_PARTS]; + +#if DEBUG_COMPLEXITY + n_entry++; +#endif + + for (i = 0; i < 16; i++) { + int len; + int lim = base + (1 << i); + /* + * this should consider the length encodings mapped to + * the particular offset. + * maybe it can be handled by subtracting from the costs. + */ + int cost = base_cost[bit] + i; + + if (lim > MAX_VALUES-1) { + if (bit < n_b-1) { + break; + } + lim = MAX_VALUES; + } + + + len = (so_far[lim] - so_far[base]) * cost; + + if (bit < n_b-1) { + if (cost_left[lim]) { +#if HAVE_CACHE + int tmp = find_cache(n_b, bit + 1, lim, min_enc); + if (tmp >= 0) { + len += tmp; +#if DEBUG_COMPLEXITY + n_hits++; +#endif + } else { + len += calc_enc(n_b, bit + 1, lim, min_enc); + } +#else + len += calc_enc(n_b, bit + 1, lim, min_enc); +#endif + } else { + /* didn't use all entries */ + break; + } + } else { + /* out of bits, return the alternative cost */ + len += cost_left[lim]; + } + + +#if DEBUG_COMPLEXITY + n_tested++; +#endif + + if (len < min_len) { + int j; + min_len = len; + enc[bit] = i; + for (j = bit+1; j < n_b; j++) { + enc[j] = min_enc[j]; + } +#if DEBUG_COMPLEXITY + n_copy++; +#endif + } + + } + +#if HAVE_CACHE + add_cache(n_b, bit, base, enc, min_len); +#endif + return min_len; +} + +static void build_arrays(Hist *h) +{ + int i; + + memset(so_far, 0, sizeof(so_far)); + memset(cost_left, 0, sizeof(cost_left)); + + int acc = 0; + for (i = 0; i < h->range; i++) { + so_far[i] = acc; + acc += h->bin[i]; + } + so_far[i] = acc; + + double cost = 0; + cost_left[h->range] = 0; + for (i = h->range-1; i >= 0; i--) { + cost += h->cost[i]; + cost_left[i] = cost; + } +} + +void optimize_enc(Hist *h, int floor, int n, prefix_t prefix, Encoding *enc) +{ + int i; + Encoding tmp; + if (!enc) { + enc = &tmp; + } + + enc->floor = floor; + enc->n = n; + enc->prefix = prefix; + + build_arrays(h); + + for (i = 0; i < n; i++) { + switch (prefix) { + case PRE_BINARY: + base_cost[i] = ceil( log(n) / log(2) ); + break; + case PRE_UNARY: + case PRE_UNARY_INV: + base_cost[i] = cost_unary(i, n); + break; + } + } + +#if DEBUG_COMPLEXITY + n_entry = 0; + n_tested = 0; + n_copy = 0; + n_hits = 0; +#endif + +#if HAVE_CACHE + invalidate_cache(); +#endif + + + char bits[MAX_PARTS]; + for (i = 0; i < n; i++) { + bits[i] = 0; + } + + calc_enc(n, 0, floor, bits); + + + for (i = 0; i < n; i++) { + enc->parts[i] = bits[i]; + } + +#if DEBUG_COMPLEXITY + printf(" n_entry=%zu, n_tested=%zu, n_copy=%zu, n_hits=%zu\n", n_entry, n_tested, n_copy, n_hits); +#endif +} + + +/************************************************************************** + * + * NAME print_enc, print_enc_long + * + * DESCRIPTION + * Print out encoding + * + ******/ +void print_enc(Encoding *enc) +{ + int i; + + for (i = 0; i < enc->n; i++) { + printf("%X", enc->parts[i]); + } +} + +void print_enc_long(Encoding *enc) +{ + int i; + int lim = enc->floor; + + printf("enc = { "); + for (i = 0; i < enc->n; i++) { + lim += 1 << enc->parts[i]; + if (i != 0) { + printf(", "); + } + printf("%d", enc->parts[i]); + } + lim--; + + printf(" } %d..%d\n", enc->floor, lim); +} + + +/************************************************************************** + * + * SECTION encoding + * + ******/ +int cost_enc(Encoding *enc, int v) +{ + int i, of, base; + + base = enc->floor; + of = -1; + for (i = 0; i < enc->n; i++) { + base += 1 << enc->parts[i]; + if (v < base) { + of = i; + break; + } + } + if (of < 0) { + /* fault */ + return 0x100000; + } + + switch (enc->prefix) { + case PRE_BINARY: + return ceil( log(enc->n) / log(2) ) + enc->parts[of]; + case PRE_UNARY: + case PRE_UNARY_INV: + return cost_unary(of, enc->n) + enc->parts[of]; + default: + /* should never happen */ + return 0x100000; + } +} + + + +void write_enc(BitWriteState *bws, Encoding *enc, int v) +{ + int i, of, base; + + base = enc->floor; + of = -1; + for (i = 0; i < enc->n; i++) { + base += 1 << enc->parts[i]; + if (v < base) { + of = i; + break; + } + } + if (of < 0) { + /* fault */ + //return 0x100000; + } + + switch (enc->prefix) { + case PRE_BINARY: + bitwr_write(bws, of, ceil( log(enc->n) / log(2) )); + break; + case PRE_UNARY: + write_unary(bws, of, enc->n, 0); + break; + case PRE_UNARY_INV: + write_unary(bws, of, enc->n, 1); + break; + } + bitwr_write(bws, v-base, enc->parts[of]); +} + + +int read_enc(BitReadState *brs, Encoding *enc) +{ + int i, of, base; + + switch (enc->prefix) { + case PRE_BINARY: + of = bitrd_read(brs, ceil( log(enc->n) / log(2) )); + break; + case PRE_UNARY: + of = read_unary(brs, enc->n, 0); + break; + case PRE_UNARY_INV: + of = read_unary(brs, enc->n, 1); + break; + default: + /* should never happen */ + of = 0; + break; + } + + base = enc->floor; + for (i = 0; i < of; i++) { + base += 1 << enc->parts[i]; + } + + return base + bitrd_read(brs, enc->parts[of]); +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/bits-base.h b/loader/tools/subsizer-0.7pre1/src/bits-base.h new file mode 100644 index 0000000..d2b8436 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/bits-base.h @@ -0,0 +1,42 @@ +/************************************************************************** + * + * FILE bits-base.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * bits-base optimization + * + ******/ +#ifndef BITS_BASE_H +#define BITS_BASE_H + +#include "bitfunc.h" +#include "histogram.h" + +typedef enum { + PRE_BINARY = 0, + PRE_UNARY = 1, + PRE_UNARY_INV, +} prefix_t; + +#define MAX_PARTS 16 +typedef struct { + int floor; + int n; + prefix_t prefix; + char parts[MAX_PARTS]; +} Encoding; + +void optimize_enc(Hist *h, int floor, int n, prefix_t prefix, Encoding *enc); + +void print_enc(Encoding *enc); +void print_enc_long(Encoding *enc); + +int cost_enc(Encoding *enc, int v); +void write_enc(BitWriteState *bws, Encoding *enc, int v); +int read_enc(BitReadState *brs, Encoding *enc); + + +#endif /* BITS_BASE_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/buffer.c b/loader/tools/subsizer-0.7pre1/src/buffer.c new file mode 100644 index 0000000..2f0ec12 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/buffer.c @@ -0,0 +1,164 @@ +/************************************************************************** + * + * FILE buffer.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of buffers + * + ******/ +#include +#include +#include +#include + +#include "global.h" +#include "buffer.h" +#include "utils.h" + + +/************************************************************************** + * + * NAME create_buffer, destroy_buffer + * + * DESCRIPTION + * Create/destroy buffer. + * + ******/ +Buffer *create_buffer(size_t size) +{ + Buffer *bf; + + bf = safe_malloc(sizeof(Buffer), "Buffer"); + bf->buf = safe_malloc(size, "Buffer buf"); + bf->size = size; + + bf->pos = 0; + bf->len = 0; + + return bf; +} + +void destroy_buffer(Buffer *bf) +{ + free(bf->buf); + free(bf); +} + + + +/************************************************************************** + * + * NAME create_buffer_from_file + * + * DESCRIPTION + * Create a new buffer from file. + * + ******/ +Buffer *create_buffer_from_file(const char *name) +{ + Buffer *bf; + size_t len; + FILE *fp; + + fp = fopen(name, "rb"); + if (!fp) { + panic("couldn't open file for reading"); + } + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + bf = create_buffer(len); + + fseek(fp, 0, SEEK_SET); + + uint8_t *b = bf->buf; + int l = len; + while (l > 0) { + size_t n; + n = fread(b, 1, l, fp); + b += n; + l -= n; + } + + fclose(fp); + + bf->len = len; + + return bf; +} + + +/************************************************************************** + * + * NAME write_buffer_to_file + * + * DESCRIPTION + * Write buffer contents to file. + * + ******/ +void write_buffer_to_file(Buffer *bf, const char *name) +{ + FILE *fp; + + fp = fopen(name, "wb"); + if (!fp) { + panic("couldn't open file for writing"); + } + + fwrite(bf->buf, 1, bf->len, fp); + + fclose(fp); + +} + + +/************************************************************************** + * + * NAME compare_buffer + * + * DESCRIPTION + * Compare buffer contents + * + ******/ +int compare_buffer(Buffer *bf1, Buffer *bf2) +{ + if (bf1->len != bf2->len) { + return -1; + } + + if (memcmp(bf1->buf, bf2->buf, bf1->len) != 0) { + return -1; + } + + return 0; +} + + +/************************************************************************** + * + * NAME reverse_buffer + * + * DESCRIPTION + * Reverse buffer contents + * + ******/ +void reverse_buffer(Buffer *bf) +{ + size_t i, l; + uint8_t *buf; + + l = bf->len; + buf = bf->buf; + + for (i = 0; i < l/2; i++) { + uint8_t b; + b = buf[l-i-1]; + buf[l-i-1] = buf[i]; + buf[i] = b; + } +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/buffer.h b/loader/tools/subsizer-0.7pre1/src/buffer.h new file mode 100644 index 0000000..50df282 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/buffer.h @@ -0,0 +1,36 @@ +/************************************************************************** + * + * FILE buffer.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of buffers + * + ******/ +#ifndef BUFFER_H +#define BUFFER_H + +#include +#include + +typedef struct { + uint8_t *buf; + size_t size; + + size_t pos; + size_t len; + +} Buffer; + + +Buffer *create_buffer(size_t size); +void destroy_buffer(Buffer *bf); +Buffer *create_buffer_from_file(const char *name); +void write_buffer_to_file(Buffer *bf, const char *name); +int compare_buffer(Buffer *bf1, Buffer *bf2); +void reverse_buffer(Buffer *bf); + + +#endif /* BUFFER_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/crunch_normal.c b/loader/tools/subsizer-0.7pre1/src/crunch_normal.c new file mode 100644 index 0000000..158edfe --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/crunch_normal.c @@ -0,0 +1,890 @@ +/************************************************************************** + * + * FILE crunch_normal.c + * Copyright (c) 2015, 2016, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * optimize encoding and generate + * + ******/ +#include +#include +#include +#include + +#include "bitfunc.h" +#include "bits-base.h" +#include "buffer.h" +#include "pathfinder.h" +#include "global.h" +#include "histogram.h" +#include "match.h" +#include "memory.h" +#include "message.h" +#include "utils.h" +#include "sfx/fold.h" +#include "sfx/generate_sfx.h" + +#define HAVE_THREE_BYTE 1 + +#define LEN_PARTS 16 +#define SINGLE_BYTE_PARTS 4 +#define TWO_BYTE_PARTS 16 +#define THREE_BYTE_PARTS 16 +#define LONG_MATCH_PARTS 16 + +#define MIN_MATCH 1 + + +/************************************************************************** + * + * NAME optimize_encoding + * + * DESCRIPTION + * Use statistics of the primary units on the cheapest path to calculate + * an optimized encoding. + * + ******/ +struct EncodingSet { + int endm; + Encoding bitsl; + Encoding bits1; + Encoding bits2; +#if HAVE_THREE_BYTE + Encoding bits3; +#endif + Encoding bits; +}; + + +static void print_enc_set(EncodingSet *es) +{ + print_enc(&es->bitsl); + printf(","); + print_enc(&es->bits1); + printf(","); + print_enc(&es->bits2); +#if HAVE_THREE_BYTE + printf(","); + print_enc(&es->bits3); +#endif + printf(","); + print_enc(&es->bits); + printf("\n"); +} + +static void print_enc_stats(Hist *h, Encoding *enc, char *label) +{ + printf("%s (n=%zu, e_min=%.2f): ", label, get_number(h), get_entropy(h)); + print_enc_long(enc); +} + +typedef struct { + Hist *h_len; + Hist *h_offs1; + Hist *h_offs2; + Hist *h_offs3; + Hist *h_offs; +} Stats; + +static Stats *create_stats(void) +{ + Stats *st; + + st = safe_malloc(sizeof(Stats), "Stats"); + + st->h_len = create_histogram(0x10000, 0); + st->h_offs1 = create_histogram(0x10000, 0); + st->h_offs2 = create_histogram(0x10000, 0); +#if HAVE_THREE_BYTE + st->h_offs3 = create_histogram(0x10000, 0); +#endif + st->h_offs = create_histogram(0x10000, 0); + + return st; +} + +static void destroy_stats(Stats *st) +{ + if (st) { + destroy_histogram(st->h_len); + destroy_histogram(st->h_offs1); + destroy_histogram(st->h_offs2); +#if HAVE_THREE_BYTE + destroy_histogram(st->h_offs3); +#endif + destroy_histogram(st->h_offs); + } + free(st); +} + + +static void collect_statistics(Stats *st, PrimaryPath *pp, CostFuncSet *cfs, EncodingSet *es) +{ + Hist *h_lit = create_histogram(0x100, 0); + Hist *h_lit_run = create_histogram(0x10000, 0); + Hist *h_mat_run = create_histogram(0x10000, 0); + + int src = 0; + int r_lit = 0; + int r_mat = 0; + Match *m = pp->path; + while (!is_end(m)) { + + if (debug_g) { + printf("%d: ", src); + } + if (is_match(m) && get_match_len(m) >= MIN_MATCH) { + int l = get_match_len(m); + int of = get_match_offs(m); + + if (debug_g) { + printf("match of=%d, l=%d", of, l); + } + /* accumulate histograms to optimize encoding */ + /* + * these should also record the alternative cost of allowing a + * particular offset. + * + */ + double cost_lt = cfs->cost_lit(es, l); + double cost_of = 1 + cfs->cost_moffs(es, of, l); + double cost_l = 1 + cfs->cost_mlen(es, l); + + hist_add(st->h_len, l, cost_lt - cost_of); + switch (l) { + case 1: + hist_add(st->h_offs1, of, cost_lt - cost_l); + break; + case 2: + hist_add(st->h_offs2, of, cost_lt - cost_l); + break; +#if HAVE_THREE_BYTE + case 3: + hist_add(st->h_offs3, of, cost_lt - cost_l); + break; +#endif + default: + hist_add(st->h_offs, of, cost_lt - cost_l); + break; + } + + src += l; + + /* handle histograms for literal and match runs. */ + /* + * this should probably record the first occurrance + * and fill in the repeat there when flushed. + */ + if (r_lit) { + hist_add(h_lit_run, r_lit, 0); + r_lit = 0; + } + r_mat++; + + } else { + int l = get_literal_len(m); + if (debug_g) { + printf("literal l=%d (", l); + } + while (l) { + uint8_t lit = pp->buf[src]; + if (debug_g) { + printf("$%02x", lit); + if (l > 1) { + printf(" "); + } + } + hist_add(h_lit, lit, 0); + src++; + --l; + } + if (debug_g) { + printf(")"); + } + + /* handle histograms for literal and match runs. */ + /* + * this should probably record the first occurrance + * and fill in the repeat there when flushed. + */ + if (r_mat) { + hist_add(h_mat_run, r_mat, 0); + r_mat = 0; + } + r_lit++; + } + m++; + + if (debug_g) { + printf("\n"); + } + } + /* flush any unflushed runs */ + if (r_lit) { + hist_add(h_lit_run, r_lit, 0); + } + if (r_mat) { + hist_add(h_mat_run, r_mat, 0); + } + + if (debug_g) { + printf("length=%d\n", src); + + printf("max_lit_run=%d, max_mat_run=%d\n", get_histrange(h_lit_run), get_histrange(h_mat_run)); + + size_t n_lit = get_number(h_lit); + double e_lit = get_entropy(h_lit); + printf("literals (n=%zu, e_min=%.2f), theoretical gain = %d bytes\n", n_lit, e_lit, (int)(n_lit - (n_lit * e_lit / 8.0))); + +#if 1 + printf(" val len l=1 l=2 l=3 l>3 lit\n"); + int i; + for (i = 0; i < 256; i++) { + printf(" %4d: %5zu %5zu %5zu %5zu %5zu %5zu\n", i, st->h_len->bin[i], st->h_offs1->bin[i], st->h_offs2->bin[i], st->h_offs3->bin[i], st->h_offs->bin[i], h_lit->bin[i]); + } +#endif + + } + + destroy_histogram(h_lit); + destroy_histogram(h_lit_run); + destroy_histogram(h_mat_run); + +} + +static void optimize_encoding(Stats *st, EncodingSet *es) +{ + optimize_enc(st->h_len, MIN_MATCH, LEN_PARTS, PRE_UNARY, &es->bitsl); + optimize_enc(st->h_offs1, 1, SINGLE_BYTE_PARTS, PRE_BINARY, &es->bits1); + optimize_enc(st->h_offs2, 1, TWO_BYTE_PARTS, PRE_BINARY, &es->bits2); +#if HAVE_THREE_BYTE + optimize_enc(st->h_offs3, 1, THREE_BYTE_PARTS, PRE_BINARY, &es->bits3); +#endif + optimize_enc(st->h_offs, 1, LONG_MATCH_PARTS, PRE_BINARY, &es->bits); + + /* find end marker candidate */ + es->endm = 0; /* impossible marker */ + int i; + for (i = MIN_MATCH; i < st->h_len->range; i++) { + if (st->h_len->bin[i] == 0) { + es->endm = i; + break; + } + } + +#if 1 + /* see if end marker is outside the encodable range (ugly, fixme!) */ + if (cost_enc(&es->bitsl, es->endm) > 0x1000) { + printf("Warning: end marker out of range, forcing allocation!\n"); + hist_add(st->h_len, es->endm, 1); + optimize_enc(st->h_len, MIN_MATCH, LEN_PARTS, PRE_UNARY, &es->bitsl); + } +#endif + + if (debug_g) { + print_enc_stats(st->h_len, &es->bitsl, "lengths"); + print_enc_stats(st->h_offs1, &es->bits1, "offs (l=1)"); + print_enc_stats(st->h_offs2, &es->bits2, "offs (l=2)"); +#if HAVE_THREE_BYTE + print_enc_stats(st->h_offs3, &es->bits3, "offs (l=3)"); + print_enc_stats(st->h_offs, &es->bits, "offs (l>3)"); +#else + print_enc_stats(st->h_offs, &es->bits, "offs (l>2)"); +#endif + printf("end marker: l=%d\n", es->endm); + + } + +} + + +/************************************************************************** + * + * SECTION packer + * + ******/ +static inline double cost_lit(EncodingSet *es, int l) +{ + return l * 9; +} + + +static inline double cost_mlen(EncodingSet *es, int l) +{ + return cost_enc(&es->bitsl, l); +} + +static void write_mlen(BitWriteState *bws, EncodingSet *es, int l) +{ + write_enc(bws, &es->bitsl, l); +} + +static int read_mlen(BitReadState *brs, EncodingSet *es) +{ + return read_enc(brs, &es->bitsl); +} + + +static inline double cost_moffs(EncodingSet *es, int of, int l) +{ + switch (l) { + case 1: + return cost_enc(&es->bits1, of); + case 2: + return cost_enc(&es->bits2, of); +#if HAVE_THREE_BYTE + case 3: + return cost_enc(&es->bits3, of); +#endif + default: + return cost_enc(&es->bits, of); + } +} + +static void write_moffs(BitWriteState *bws, EncodingSet *es, int of, int l) +{ + switch (l) { + case 1: + write_enc(bws, &es->bits1, of); + break; + case 2: + write_enc(bws, &es->bits2, of); + break; +#if HAVE_THREE_BYTE + case 3: + write_enc(bws, &es->bits3, of); + break; +#endif + default: + write_enc(bws, &es->bits, of); + break; + } +} + +static int read_moffs(BitReadState *brs, EncodingSet *es, int l) +{ + switch (l) { + case 1: + return read_enc(brs, &es->bits1); + case 2: + return read_enc(brs, &es->bits2); +#if HAVE_THREE_BYTE + case 3: + return read_enc(brs, &es->bits3); +#endif + default: + return read_enc(brs, &es->bits); + } +} + + +static inline int cost_endm(EncodingSet *es) +{ + return 1 + cost_mlen(es, es->endm); +} + +static void write_endm(BitWriteState *bws, EncodingSet *es) +{ + bitwr_write(bws, 0, 1); + write_mlen(bws, es, es->endm); +} + + +static CostFuncSet cfs = { + cost_lit, + cost_mlen, + cost_moffs +}; + + + +static inline double cost_lit_init(EncodingSet *es, int l) +{ + return l * 9; +} + +static inline double cost_mlen_init(EncodingSet *es, int l) +{ + return ceil( 0 + log(l) / log(2) ); +} + +static inline double cost_moffs_init(EncodingSet *es, int of, int l) +{ + if (l == 1) { + return ceil( 0 + log(of) / log(2) ); + } + return ceil( 2 + log(of) / log(2) ); +} + +static CostFuncSet cfs_init = { + cost_lit_init, + cost_mlen_init, + cost_moffs_init +}; + +static PrimaryPath *optimize_tree(MatchTree *mt, EncodingSet *es) +{ + int max_passes = 16; /* should be configurable. */ + + int i; + PrimaryPath *pp; + Stats *st; + + /* calculate initial encoding using doctored cost */ + pp = find_cheapest_path(mt, &cfs_init, es, FCP_INITIAL_LITERAL); + st = create_stats(); + collect_statistics(st, pp, &cfs_init, es); + optimize_encoding(st, es); + destroy_stats(st); + + /* + * keep iterating find_cheapest_path + optimize_encoding until encoding + * no longer changes. + */ + EncodingSet last_es; + for (i = 0; i < max_passes; i++) { + memcpy(&last_es, es, sizeof(EncodingSet)); + destroy_primarypath(pp); + + if (verbose_g) { + print_enc_set(es); + printf("end marker: l=%d (cost=%d)\n", es->endm, cost_endm(es)); + } + pp = find_cheapest_path(mt, &cfs, es, FCP_INITIAL_LITERAL); + + /* endm + parts + space of end marker */ + int overhead = 8 + 4 * (4*16 + 4) + cost_endm(es); + size_t cost = (pp->cost + overhead + 7) / 8; + + msg(MSG_VERBOSE, " %zu (left %.2f%%)\n", cost, ((100.0 * cost) / pp->len)); + st = create_stats(); + collect_statistics(st, pp, &cfs, es); + optimize_encoding(st, es); + destroy_stats(st); + + if (memcmp(es, &last_es, sizeof(EncodingSet)) == 0) { + break; + } + } + + /* check for impossible marker */ + if (es->endm == 0) { + fprintf(stderr, "error: couldn't find an end marker!\n"); + } + + return pp; +} + + +/************************************************************************** + * + * NAME generate + * + * DESCRIPTION + * Generate packed output from optimized structure. + * + ******/ +static int generate(PrimaryPath *pp, EncodingSet *es, uint8_t *buf, int flags) +{ + int i; + BitWriteState bws; + int l; + + bitwr_init(&bws, buf, BITMODE_SIDEBYTE | flags); + + bitwr_write8s(&bws, es->endm); + + for (i = 0; i < es->bitsl.n; i++) { + bitwr_write(&bws, es->bitsl.parts[i], 4); + } + for (i = 0; i < es->bits2.n; i++) { + bitwr_write(&bws, es->bits2.parts[i], 4); + } +#if HAVE_THREE_BYTE + for (i = 0; i < es->bits3.n; i++) { + bitwr_write(&bws, es->bits3.parts[i], 4); + } +#endif + for (i = 0; i < es->bits.n; i++) { + bitwr_write(&bws, es->bits.parts[i], 4); + } + for (i = 0; i < es->bits1.n; i++) { + bitwr_write(&bws, es->bits1.parts[i], 4); + } + + l = 0; + Match *m = pp->path; + + /* initial literal */ + if (is_literal(m) && get_literal_len(m) == 1) { + bitwr_write8s(&bws, pp->buf[l]); + l++; + m++; + } else { + panic("internal fault"); + } + + while (!is_end(m)) { + + if (is_match(m)) { + /* match */ + bitwr_write(&bws, 0, 1); + write_mlen(&bws, es, get_match_len(m)); + write_moffs(&bws, es, get_match_offs(m), get_match_len(m)); + + l += get_match_len(m); + } else { + int n = get_literal_len(m); + while (n) { + /* literal */ + bitwr_write(&bws, 1, 1); + bitwr_write8s(&bws, pp->buf[l]); + l++; + --n; + } + } + m++; + } + + /* end marker */ + write_endm(&bws, es); + + return bitwr_flush(&bws); +} + + +/************************************************************************** + * + * NAME crunch_normal_int + * + * DESCRIPTION + * Common cruncher parts + * + ******/ +static int crunch_normal_int(Buffer *sbf, Buffer *dbf, int flags) +{ + MatchTree *mt = create_matchtree(); + double t1, t2; + EncodingSet es; + memset(&es, 0, sizeof(EncodingSet)); + + msg(MSG_VERBOSE, "build matches...\n"); + t1 = get_time(); + build_match(mt, sbf->buf, sbf->len); + t2 = get_time(); + msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1); + + msg(MSG_VERBOSE, "optimizing matches...\n"); + t1 = get_time(); + PrimaryPath *pp = optimize_tree(mt, &es); + t2 = get_time(); + msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1); + + destroy_matchtree(mt); + + msg(MSG_VERBOSE, "generating output...\n"); + t1 = get_time(); + dbf->len = generate(pp, &es, dbf->buf, flags); + t2 = get_time(); + msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1); + + destroy_primarypath(pp); + + return 0; +} + + +/************************************************************************** + * + * NAME decrunch_normal_int + * + * DESCRIPTION + * Common decruncher parts + * + ******/ +static int decrunch_normal_int(Buffer *sbf, Buffer *dbf, int flags, size_t *safep) +{ + int i; + BitReadState brs; + int cur; + EncodingSet es; + int safe; + int spos; + uint8_t *destbuf = dbf->buf; + + bitrd_init(&brs, sbf->buf, BITMODE_SIDEBYTE | flags); + + es.endm = bitrd_read8s(&brs); + + es.bitsl.floor = MIN_MATCH; + es.bitsl.n = LEN_PARTS; + es.bitsl.prefix = PRE_UNARY; + for (i = 0; i < es.bitsl.n; i++) { + es.bitsl.parts[i] = bitrd_read(&brs, 4); + } + + es.bits2.floor = 1; + es.bits2.n = TWO_BYTE_PARTS; + es.bits2.prefix = PRE_BINARY; + for (i = 0; i < es.bits2.n; i++) { + es.bits2.parts[i] = bitrd_read(&brs, 4); + } + +#if HAVE_THREE_BYTE + es.bits3.floor = 1; + es.bits3.n = THREE_BYTE_PARTS; + es.bits3.prefix = PRE_BINARY; + for (i = 0; i < es.bits3.n; i++) { + es.bits3.parts[i] = bitrd_read(&brs, 4); + } +#endif + + es.bits.floor = 1; + es.bits.n = LONG_MATCH_PARTS; + es.bits.prefix = PRE_BINARY; + for (i = 0; i < es.bits.n; i++) { + es.bits.parts[i] = bitrd_read(&brs, 4); + } + + es.bits1.floor = 1; + es.bits1.n = SINGLE_BYTE_PARTS; + es.bits1.prefix = PRE_BINARY; + for (i = 0; i < es.bits1.n; i++) { + es.bits1.parts[i] = bitrd_read(&brs, 4); + } + + + cur = 0; + safe = 0; + spos = dbf->len - sbf->len; /* extremely ugly: assumes dbf contains the unpacked data */ + while (1) { + if (cur == 0 || bitrd_read(&brs, 1)) { + uint8_t c = bitrd_read8s(&brs); + destbuf[cur] = c; + cur++; + } else { + int len, offs; + len = read_mlen(&brs, &es); + if (len == es.endm) { + break; + } + offs = read_moffs(&brs, &es, len); + if (offs > cur) { + fprintf(stderr, "error: offset out of range\n"); + break; + } + for (i = 0; i < len; i++) { + destbuf[cur] = destbuf[cur-offs]; + cur++; + } + } + /* check safe distance */ + int dist = cur - (brs.pos + spos); + if (dist > safe) { + safe = dist; + } + } + dbf->len = cur; + + msg(MSG_DEBUG, "safe = %d\n", safe); + + if (safep) { + *safep = safe; + } + + return 0; +} + + +/************************************************************************** + * + * NAME crunch_normal + * + * DESCRIPTION + * Cruncher for the "normal" algorithm + * + ******/ +int crunch_normal(Buffer *sbf, Buffer *dbf) +{ + return crunch_normal_int(sbf, dbf, 0); +} + + +/************************************************************************** + * + * NAME decrunch_normal + * + * DESCRIPTION + * Decruncher for the "normal" algorithm + * + ******/ +int decrunch_normal(Buffer *sbf, Buffer *dbf) +{ + return decrunch_normal_int(sbf, dbf, 0, 0); +} + + +/************************************************************************** + * + * NAME crunch_normal_mem + * + * DESCRIPTION + * Memory cruncher for the "normal" algorithm + * + ******/ +int crunch_normal_mem(Memory *smem, Memory *dmem, int num_opts, char **opts) +{ + int ret; + Buffer *sbf; + Buffer *dbf; + size_t safe = 0; + mem_ptr_t sa, ea, ca; + uint8_t endm, first; + int forwards_mode = num_opts; // FIXME: kludge!!!! + + sbf = create_buffer(smem->high - smem->low); + sbf->len = smem->high - smem->low; + memcpy(sbf->buf, smem->buf + smem->low, sbf->len); + dbf = create_buffer(0x100000); + + if (!forwards_mode) { + /* crunch in reverse */ + reverse_buffer(sbf); + ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT); + + /* check safe distance (reuse sbf as target) */ + decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe); + destroy_buffer(sbf); + + /* reverse result */ + reverse_buffer(dbf); + } else { + /* crunch forwards */ + ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT); + + /* check safe distance (reuse sbf as target) */ + decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe); + destroy_buffer(sbf); + } + + msg(MSG_VERBOSE, "safe = %zu\n", safe); + + //sa = 0x1000; + if (!forwards_mode) { + /* default output address is the minimum margin required for safe uncrunch */ + sa = smem->low - safe; + + /* generate output */ + ca = sa; + ca += insert_mem(dmem, sa, dbf->buf, dbf->len); + + /* pop first */ + first = get_byte(dmem, --ca); + /* pop endm */ + endm = get_byte(dmem, --ca); + + /* add first */ + set_byte(dmem, ca++, first); + /* add target address (in reverse) */ + set_byte(dmem, ca++, (smem->high) & 0xff); + set_byte(dmem, ca++, (smem->high) >> 8); + /* add endm (adjusted) */ + set_byte(dmem, ca++, endm - 1); + + ea = ca; + } else { + /* default output address is the minimum margin required for safe uncrunch */ + ea = smem->high + safe; + + /* generate output */ + ca = ea - dbf->len; + insert_mem(dmem, ca, dbf->buf, dbf->len); + + /* pop first */ + first = get_byte(dmem, ca++); + /* pop endm */ + endm = get_byte(dmem, ca++); + + /* add first */ + set_byte(dmem, --ca, first); + /* add target address (in reverse) */ + set_byte(dmem, --ca, (smem->low) & 0xff); + set_byte(dmem, --ca, (smem->low) >> 8); + /* add endm (adjusted) */ + set_byte(dmem, --ca, endm - 1); + + sa = ca; + } + + dmem->low = sa; + dmem->high = ea; + + destroy_buffer(dbf); + + return ret; +} + + +/************************************************************************** + * + * NAME crunch_normal_sfx + * + * DESCRIPTION + * Executable cruncher for the "normal" algorithm + * + ******/ +int crunch_normal_sfx(Memory *smem, Memory *dmem, int num_opts, char **opts, int jmp) +{ + int ret; + Buffer *sbf; + Buffer *dbf; + int endm; + uint8_t tmp; + size_t safe = 0; + SfxConfig *conf; + + /* get initial options */ + conf = prepare_sfx(num_opts, opts, jmp); + + /* fold if applicable */ + if (conf->fold) { + /* this should probably not change smem */ + conf = fold(smem, 0, conf->fold, 0x10000, conf); + } + + sbf = create_buffer(smem->high - smem->low); + sbf->len = smem->high - smem->low; + memcpy(sbf->buf, smem->buf + smem->low, sbf->len); + dbf = create_buffer(0x100000); + + /* crunch in reverse */ + reverse_buffer(sbf); + ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT); + + /* check safe distance (reuse sbf as target) */ + decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe); + destroy_buffer(sbf); + + /* reverse result */ + reverse_buffer(dbf); + + /* pop endm from the end of the stream */ + tmp = dbf->buf[dbf->len-1]; + endm = dbf->buf[dbf->len-2]; + dbf->buf[dbf->len-2] = tmp; + dbf->len--; + + msg(MSG_VERBOSE, "safe = %zu\n", safe); + + /* generate output */ + generate_sfx(dbf, dmem, smem, safe, endm, conf); + + destroy_buffer(dbf); + + return ret; +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/crunch_normal.h b/loader/tools/subsizer-0.7pre1/src/crunch_normal.h new file mode 100644 index 0000000..18815c0 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/crunch_normal.h @@ -0,0 +1,25 @@ +/************************************************************************** + * + * FILE crunch_normal.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * create encoding structure + * + ******/ +#ifndef CRUNCH_NORMAL_H +#define CRUNCH_NORMAL_H + +#include + +#include "buffer.h" +#include "memory.h" + +int crunch_normal(Buffer *sbf, Buffer *dbf); +int decrunch_normal(Buffer *sbf, Buffer *dbf); +int crunch_normal_mem(Memory *smem, Memory *dmem, int num_opts, char **opts); +int crunch_normal_sfx(Memory *smem, Memory *dmem, int num_opts, char **opts, int jmp); + +#endif /* CRUNCH_NORMAL_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/global.c b/loader/tools/subsizer-0.7pre1/src/global.c new file mode 100644 index 0000000..25ed5ce --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/global.c @@ -0,0 +1,33 @@ +/************************************************************************** + * + * FILE global.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Global functions. + * + ******/ +#include +#include +#include + +#include "global.h" + +/* global variables */ +int verbose_g; +int debug_g; + +void panic(const char *str, ...) +{ + va_list args; + + fprintf(stderr, "%s: ", program_g); + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); + fputc('\n', stderr); + exit(1); +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/global.h b/loader/tools/subsizer-0.7pre1/src/global.h new file mode 100644 index 0000000..0752222 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/global.h @@ -0,0 +1,27 @@ +/************************************************************************** + * + * FILE global.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Global functions. + * + ******/ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include + +#define PACKAGE "subsizer" +#define VERSION "0.7pre1" + +/* global variables */ +extern const char program_g[]; +extern int verbose_g; +extern int debug_g; + +void panic(const char *str, ...); + +#endif /* GLOBAL_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/histogram.c b/loader/tools/subsizer-0.7pre1/src/histogram.c new file mode 100644 index 0000000..9180b4a --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/histogram.c @@ -0,0 +1,181 @@ +/************************************************************************** + * + * FILE histogram.c + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of histograms + * + ******/ +#include +#include +#include +#include + +#include "global.h" +#include "histogram.h" +#include "utils.h" + + +Hist *create_histogram(int range, int window) +{ + Hist *h; + + h = safe_malloc(sizeof(Hist), "Histogram"); + h->range = range; + h->bin = safe_calloc(range, sizeof(size_t), "histogram bins"); + h->cost = safe_calloc(range, sizeof(double), "histogram costs"); + + h->window = window; + h->wbuf = 0; + h->wcnt = 0; + if (h->window) { + h->wbuf = safe_calloc(h->window, sizeof(HistEntry), "histogram window"); + } + + h->he = 0; + return h; +} + +void destroy_histogram(Hist *h) +{ + free(h->he); + free(h->wbuf); + free(h->bin); + free(h->cost); + free(h); +} + +void hist_reset(Hist *h) +{ + int i; + for (i = 0; i < h->range; i++) { + h->bin[i] = 0; + h->cost[i] = 0; + } +} + + +void hist_add(Hist *h, int v, double cost) +{ + h->bin[v]++; + h->cost[v] += cost; + + if (h->wbuf) { + HistEntry *he = &h->wbuf[h->wcnt]; + if (he->n) { + h->bin[he->val]--; + h->cost[he->val] -= he->cost; + } + + he->n = 1; + he->val = v; + he->cost = cost; + + h->wcnt = (h->wcnt + 1) % h->window; + } +} + + +HistEntry *get_histlist(Hist *h) +{ + int i; + int n; + HistEntry *he = h->he; + + if (he) { + return he; + } + + n = 0; + for (i = 0; i < h->range; i++) { + if (h->bin[i]) { + n++; + } + } + + he = safe_malloc(sizeof(HistEntry) * (n + 1), "histogram list"); + h->he = he; + for (i = 0; i < h->range; i++) { + if (h->bin[i]) { + he->val = i; + he->n = h->bin[i]; + he->cost = h->cost[i]; + he++; + } + } + he->n = 0; + + return h->he; +} + + +/************************************************************************** + * + * NAME get_histrange + * + * DESCRIPTION + * get the actual range used. + * + ******/ +int get_histrange(Hist *h) +{ + int i; + for (i = h->range-1; i >= 0; --i) { + if (h->bin[i]) + break; + } + + return i; +} + +/************************************************************************** + * + * NAME get_number + * + * DESCRIPTION + * get the number of values added. + * + ******/ +size_t get_number(Hist *h) +{ + int i; + size_t n; + + n = 0; + for (i = 0; i < h->range; i++) { + n += h->bin[i]; + } + + return n; +} + +/************************************************************************** + * + * NAME get_entropy + * + * DESCRIPTION + * Calculate the Shannon entropy of a histogram + * + ******/ +double get_entropy(Hist *h) +{ + int i; + double e; + + size_t n = get_number(h); + + e = 0.0; + for (i = 0; i < h->range; i++) { + if (h->bin[i]) { + double p = (double)h->bin[i] / n; + e += -(log(p)/log(2)) * p; + } + } + + return e; +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/histogram.h b/loader/tools/subsizer-0.7pre1/src/histogram.h new file mode 100644 index 0000000..a1e7add --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/histogram.h @@ -0,0 +1,45 @@ +/************************************************************************** + * + * FILE histogram.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of histograms + * + ******/ +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +#include +#include + +typedef struct { + int val; + size_t n; + double cost; +} HistEntry; + +typedef struct { + int range; + size_t *bin; + double *cost; + + int window; + HistEntry *wbuf; + int wcnt; + + HistEntry *he; +} Hist; + +Hist *create_histogram(int range, int window); +void destroy_histogram(Hist *h); +void hist_reset(Hist *h); +void hist_add(Hist *h, int v, double cost); +HistEntry *get_histlist(Hist *h); +int get_histrange(Hist *h); +size_t get_number(Hist *h); +double get_entropy(Hist *h); + +#endif /* HISTOGRAM_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/match.c b/loader/tools/subsizer-0.7pre1/src/match.c new file mode 100644 index 0000000..1ef7d9f --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/match.c @@ -0,0 +1,220 @@ +/************************************************************************** + * + * FILE match.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * create match structure + * + ******/ +#include +#include +#include + +#include "global.h" +#include "match.h" +#include "utils.h" + + +/************************************************************************** + * + * SECTION packer + * + ******/ + +MatchTree *create_matchtree(void) +{ + MatchTree *mt; + + mt = safe_malloc(sizeof(MatchTree), "MatchTree"); + mt->match = 0; + mt->match_buf = 0; + + mt->min_offs = 1; + mt->max_offs = 0x10000; + + /* shortest match is l=1 */ + /* 1 + 1 + 1 + 5 bits is less than 1 * literal */ + //mt->min_offs1 = 1; + mt->max_offs1 = 32; +#if 0 + /* 1 + 2 + 1 + 13 bits is less than 2 * literal */ + //mt->min_offs2 = 1; + mt->max_offs2 = 0x2000; +#else + /* shortest match is l=2 */ + /* 1 + 1 + 1 + 14 bits is less than 2 * literal */ + //mt->min_offs2 = 1; + mt->max_offs2 = 0x4000; +#endif + mt->min_len = 1; + mt->max_len = 0x100; + mt->min_rle = 2; + mt->max_rle = 0x100; + mt->rle_holdoff = 8; + //mt->rle_holdoff = 1000000; + + /* maybe allocate entries for the actual buffer already here? */ + + //printf("sizeof(Match) = %d\n", sizeof(Match)); + + return mt; +} + +void destroy_matchtree(MatchTree *mt) +{ + if (mt) { + free(mt->match); + free(mt->match_buf); + free(mt); + } +} + + +int build_match(MatchTree *mt, uint8_t *buf, int len) +{ + int cur; + int rcnt; + + mt->buf = buf; + mt->len = len; + mt->match = safe_malloc(len * sizeof(Match *), "match table"); + + /* this should be replaced by some dynamic realloc! */ + mt->match_buf = safe_malloc(200000000 * sizeof(Match), "matches"); + + + Match *cur_m = mt->match_buf; + + /* do the processing */ + rcnt = 0; + cur = 0; + while (cur < len) { + int i; + int window; + int rlen; + Match **mp = &mt->match[cur]; + uint8_t v = buf[cur]; + Match *last_m = cur_m; + + /* find matches */ + *mp = 0; + + /* check rle */ + rlen = 1; + while ((cur + rlen < len) && buf[cur + rlen] == v) { + rlen++; + if (rlen == mt->max_rle) { + break; + } + } + if (rlen >= mt->min_rle) { + /* skip the first rle */ + if (rcnt > 0) { + make_rle(cur_m, cur, rlen); + cur_m++; + } + rcnt++; + if (rcnt > mt->rle_holdoff) { + goto cont; /* gaah! Clean me up! */ + } + } else { + rcnt = 0; + } + + /* max search range from the current offset */ + window = (cur < mt->max_offs) ? cur : mt->max_offs; + + /* seek for matches within that window */ + for (i = mt->min_offs; i <= window; i++) { + int moffs = i; + int mlen = 0; + + /* + * optimized search, first check if at least one byte matches, + * then start scanning with end check and similar + */ + if (buf[cur-i] == v) { + mlen++; + + while ((cur+mlen < len) && buf[cur-i+mlen] == buf[cur+mlen]) { + mlen++; + if (mlen == mt->max_len) { + break; + } + } + } + + /* this should probably consider the min_len. */ + if ( (mlen >= mt->min_len) && ( + (mlen >= 1 && moffs <= mt->max_offs1) || + (mlen >= 2 && moffs <= mt->max_offs2) || + (mlen >= 3) ) + ) { + make_match(cur_m, moffs, mlen); + cur_m++; + } + + } + + cont: + if (last_m != cur_m) { + make_end(cur_m); + cur_m++; + *mp = last_m; + } + + cur++; + } + + if (debug_g) { + int n_tot = 0, n_rle_tot = 0; + for (cur = 0; cur < len; cur++) { + printf("%d: $%02x, ", cur, mt->buf[cur]); + if (mt->match[cur] == 0) { + printf("literal\n"); + } else { + int n = 0, n_rle = 0; + int max_match = 0, max_rle = 0; + Match *m = mt->match[cur]; + while (m && !is_end(m)) { + if (is_rle(m)) { + int l = get_rle_len(m); + if (l > max_rle) { + max_rle = l; + } + n_rle++; + } else if (is_match(m)) { + int l = get_match_len(m); + if (l > max_match) { + max_match = l; + } + n++; + } + m++; + } + n_tot += n; + n_rle_tot += n_rle; + if (n_rle) { + printf("%d rle (l=%d)", n_rle, max_rle); + } + if (n) { + if (n_rle) { + printf(", "); + } + printf("%d matches (l=%d)", n, max_match); + } + printf("\n"); + } + } + printf("%d total rle\n", n_rle_tot); + printf("%d total matches\n", n_tot); + } + + return 0; +} + + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/match.h b/loader/tools/subsizer-0.7pre1/src/match.h new file mode 100644 index 0000000..a663081 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/match.h @@ -0,0 +1,134 @@ +/************************************************************************** + * + * FILE match.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * create match structure + * + ******/ +#ifndef MATCH_H +#define MATCH_H + +#include + + + +typedef struct { + /* + * MTYPE_END: ? + * MTYPE_LITERAL: ? + * MTYPE_MATCH: ? + * MTYPE_RLE: ? + */ +#define MTYPE_END 0 +#define MTYPE_LITERAL 1 +#define MTYPE_MATCH 2 +#define MTYPE_RLE 3 + + uint32_t type:8; + uint32_t len:24; + uint32_t offs; +} Match; + + + +static inline void make_end(Match *m) +{ + m->type = MTYPE_END; +} + +static inline int is_end(Match *m) +{ + return m->type == MTYPE_END; +} + + +static inline void make_rle(Match *m, uint32_t src, int len) +{ + m->type = MTYPE_RLE; + m->offs = src; + m->len = len; +} + +static inline int is_rle(Match *m) +{ + return m->type == MTYPE_RLE; +} +static inline int get_rle_len(Match *m) +{ + return m->len; +} +static inline int get_rle_src(Match *m) +{ + return m->offs; +} + + +static inline void make_match(Match *m, uint32_t offs, int len) +{ + m->type = MTYPE_MATCH; + m->len = len; + m->offs = offs; +} + +static inline int is_match(Match *m) +{ + return m->type == MTYPE_MATCH; +} +static inline int get_match_len(Match *m) +{ + return m->len; +} +static inline int get_match_offs(Match *m) +{ + return m->offs; +} + + +static inline void make_literal(Match *m, uint32_t src, int len) +{ + m->type = MTYPE_LITERAL; + m->offs = src; + m->len = len; +} + +static inline int is_literal(Match *m) +{ + return m->type == MTYPE_LITERAL; +} +static inline int get_literal_len(Match *m) +{ + return m->len; +} +static inline int get_literal_src(Match *m) +{ + return m->offs; +} + +typedef struct { + int min_offs; + int max_offs; + int max_offs1; + int max_offs2; + int min_len; + int max_len; + int min_rle; + int max_rle; + int rle_holdoff; + + uint8_t *buf; + int len; + Match **match; + Match *match_buf; +} MatchTree; + + +MatchTree *create_matchtree(void); +void destroy_matchtree(MatchTree *mt); + +int build_match(MatchTree *mt, uint8_t *srcbuf, int srclen); + +#endif /* MATCH_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/memory.c b/loader/tools/subsizer-0.7pre1/src/memory.c new file mode 100644 index 0000000..db220bf --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/memory.c @@ -0,0 +1,253 @@ +/************************************************************************** + * + * FILE memory.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of memory layouts + * + ******/ +#include +#include +#include +#include + +#include "global.h" +#include "memory.h" +#include "message.h" +#include "utils.h" + + +/************************************************************************** + * + * NAME create_memory, destroy_memory + * + * DESCRIPTION + * Create/destroy memory. + * + ******/ +Memory *create_memory(size_t size) +{ + Memory *mem; + + mem = safe_malloc(sizeof(Memory), "Memory"); + mem->buf = safe_calloc(size, 1, "Memory buf"); + mem->size = size; + + mem->low = -1; + mem->high = -1; + + return mem; +} + +void destroy_memory(Memory *mem) +{ + free(mem->buf); + free(mem); +} + + +/************************************************************************** + * + * NAME get_byte, set_byte, get_word, set_word + * + * DESCRIPTION + * Memory accessors. + * + ******/ +uint8_t get_byte(Memory *mem, mem_ptr_t ad) +{ + return mem->buf[ad & (mem->size-1)]; +} + + +void set_byte(Memory *mem, mem_ptr_t ad, uint8_t val) +{ + mem->buf[ad & (mem->size-1)] = val; +} + +uint16_t get_word(Memory *mem, mem_ptr_t ad) +{ + return get_byte(mem, ad) | (get_byte(mem, ad+1) << 8); +} + +void set_word(Memory *mem, mem_ptr_t ad, uint16_t val) +{ + set_byte(mem, ad, val & 0xff); + set_byte(mem, ad+1, val >> 8); +} + + +size_t insert_mem(Memory *dmem, mem_ptr_t da, uint8_t *src, size_t len) +{ + memcpy(dmem->buf + da, src, len); + + return len; +} + + +/************************************************************************** + * + * NAME load_mem + * + * DESCRIPTION + * load a file to memory. + * + ******/ +void load_mem(Memory *mem, file_t *f, mem_ptr_t *aptr, size_t *lptr) +{ + FILE *fp; + size_t lrd; + mem_ptr_t ad; + mem_ptr_t la; + int c; + + fp = fopen(f->name, "rb"); + if (!fp) { + panic("couldn't open source file"); + } + + la = f->la; + + switch (f->mode) { + case MODE_NORMAL: + /* get load address */ + la = fgetc(fp) + (fgetc(fp) << 8); + break; + case MODE_NEWADDR: + /* skip load address */ + fgetc(fp); + fgetc(fp); + break; + case MODE_RAW: + /* no load address */ + break; + default: + break; + } + + /* skip offset if any */ + if (f->offs > 0) { + fseek(fp, f->offs, SEEK_CUR); + } + + /* load file body */ + ad = la; + lrd = 0; + while ( c = fgetc(fp), c != EOF ) { + set_byte(mem, ad, c); + ad++; + lrd++; + /* if a max len is specified, then terminate when it has been + reached. */ + if (f->len > 0 && lrd >= f->len) + break; + } + + fclose(fp); + + msg(MSG_VERBOSE, "read '%s' $%04X-$%04X.\n", f->name, la, ad); + + + if (mem->low < 0 || mem->low > la) { + mem->low = la; + } + if (mem->high < 0 || mem->high < ad) { + mem->high = ad; + } + + if (aptr) { + *aptr = la; + } + if (lptr) { + *lptr = lrd; + } +} + + +/************************************************************************** + * + * NAME load_file_to_memory + * + * DESCRIPTION + * load a file to memory. + * + ******/ +void load_file_to_memory(Memory *mem, const char *name) +{ + size_t len; + FILE *fp; + size_t la; + size_t sa; + size_t ea; + + fp = fopen(name, "rb"); + if (!fp) { + panic("couldn't open file for reading"); + } + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + + fseek(fp, 0, SEEK_SET); + la = fgetc(fp) | (fgetc(fp) << 8); + len -= 2; + + sa = la; + ea = sa + len; + + uint8_t *b = mem->buf + sa; + int l = len; + while (l > 0) { + size_t n; + n = fread(b, 1, l, fp); + b += n; + l -= n; + } + + fclose(fp); + + if (mem->low < 0 || mem->low > sa) { + mem->low = sa; + } + if (mem->high < 0 || mem->high < ea) { + mem->high = ea; + } +} + + +/************************************************************************** + * + * NAME save_file_from_memory + * + * DESCRIPTION + * Write memory contents to file. + * + ******/ +void save_file_from_memory(Memory *mem, const char *name) +{ + FILE *fp; + size_t la; + size_t sa; + size_t ea; + + sa = mem->low; + ea = mem->high; + la = sa; + + fp = fopen(name, "wb"); + if (!fp) { + panic("couldn't open file for writing"); + } + + fputc(la & 0xff, fp); + fputc(la >> 8, fp); + fwrite(mem->buf + sa, 1, ea - sa, fp); + + fclose(fp); + +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/memory.h b/loader/tools/subsizer-0.7pre1/src/memory.h new file mode 100644 index 0000000..31fe64a --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/memory.h @@ -0,0 +1,61 @@ +/************************************************************************** + * + * FILE memory.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * handling of memory layouts + * + ******/ +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include + +typedef uint32_t mem_ptr_t; + +typedef struct { + uint8_t *buf; + size_t size; + + int low; + int high; +} Memory; + + +Memory *create_memory(size_t size); +void destroy_memory(Memory *mem); + +uint8_t get_byte(Memory *mem, mem_ptr_t ad); +void set_byte(Memory *mem, mem_ptr_t ad, uint8_t val); +uint16_t get_word(Memory *mem, mem_ptr_t ad); +void set_word(Memory *mem, mem_ptr_t ad, uint16_t val); + +size_t insert_mem(Memory *dmem, mem_ptr_t da, uint8_t *src, size_t len); + + + +enum mode_t { + MODE_NORMAL = 0, + MODE_NEWADDR, + MODE_RAW +}; + +typedef struct { + char *name; + enum mode_t mode; + mem_ptr_t la; + int offs; + int len; +} file_t; + +void load_mem(Memory *mem, file_t *f, mem_ptr_t *aptr, size_t *lptr); + +void load_file_to_memory(Memory *mem, const char *name); +void save_file_from_memory(Memory *mem, const char *name); + + +#endif /* MEMORY_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/message.c b/loader/tools/subsizer-0.7pre1/src/message.c new file mode 100644 index 0000000..74323dd --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/message.c @@ -0,0 +1,50 @@ +/************************************************************************** + * + * FILE message.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Message handling + * + ******/ +#include +#include + +#include "global.h" +#include "message.h" + + +void vmsg(msg_level_t level, const char *s, va_list ap) +{ + switch (level) { + case MSG_DEBUG: + if (!debug_g) { + return; + } + break; + case MSG_VERBOSE: + if (!verbose_g) { + return; + } + break; + default: + break; + } + + vprintf(s, ap); +} + + +void msg(msg_level_t level, const char *s, ...) +{ + va_list ap; + va_start(ap, s); + + vmsg(level, s, ap); + + va_end(ap); +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/message.h b/loader/tools/subsizer-0.7pre1/src/message.h new file mode 100644 index 0000000..b0fd240 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/message.h @@ -0,0 +1,22 @@ +/************************************************************************** + * + * FILE message.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Message handling. + * + ******/ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include + +typedef enum { MSG_DEBUG, MSG_VERBOSE } msg_level_t; + +void msg(msg_level_t level, const char *s, ...); +void vmsg(msg_level_t level, const char *s, va_list ap); + +#endif /* MESSAGE_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/params.c b/loader/tools/subsizer-0.7pre1/src/params.c new file mode 100644 index 0000000..b8491d3 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/params.c @@ -0,0 +1,155 @@ +/************************************************************************** + * + * FILE params.c + * Copyright (c) 2012, 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * parameter parsing + * + ******/ +#include +#include +#include + +#include "memory.h" +#include "global.h" +#include "params.h" + + + +static unsigned long int my_strtoul(char *ptr, char **eptr) +{ + int base; + unsigned long int v; + + switch (*ptr) { + case '$': + base = 16; + ptr++; + break; + case '@': + base = 8; + ptr++; + break; + case '%': + base = 2; + ptr++; + break; + default: + base = 0; + break; + } + + v = strtoul(ptr, eptr, base); + + return v; +} + + +int parse_value(char *str) +{ + char *eptr; + unsigned long int v; + + v = my_strtoul(str, &eptr); + if ( str == eptr || *eptr != 0 ) + panic("couldn't parse value (%s)", str); + + return v; +} + +void parse_range(char *str, mem_ptr_t *low, mem_ptr_t *high) +{ + char *p; + if ( !(str && strlen(str)) ) { + return; + } + + p = strchr(str, '-'); + if (p) { + *p++ = 0; + if (*p != 0) { + *high = parse_value(p); + } + } + if (*str != 0) { + *low = parse_value(str); + } +} + +file_t parse_filename(char *name) +{ + char *p, *name_end; + char c; + char *eptr; + unsigned long int v; + file_t file; + + file.name = name; + file.mode = MODE_NORMAL; + file.la = 0; + file.offs = -1; + file.len = -1; + + + p = name; + /* parse */ + p = strpbrk(p, ",@"); + if (p) { + name_end = p; + c = *p++; + + switch (c) { + case ',': + v = my_strtoul(p, &eptr); + if (p == eptr && *p != ',') { + panic("missing start address"); + } + if (p != eptr) { + file.la = v; + file.mode = MODE_NEWADDR; + } + + p = eptr; + break; + case '@': + file.la = my_strtoul(p, &eptr); + if (p == eptr) { + panic("missing start address"); + } + file.mode = MODE_RAW; + + p = eptr; + break; + default: + break; + } + + p = strchr(p, ','); + if (p) { + p++; + v = my_strtoul(p, &eptr); + if (p != eptr) { + file.offs = v; + } + + p = strchr(p, ','); + if (p) { + p++; + file.len = parse_value(p); + } + } + + /* mark the end of the file name */ + *name_end = 0; + } + + if (debug_g) { + printf("name='%s', ad=$%04X, offs=%d, len=%d, mode=%d\n", file.name, file.la, file.offs, file.len, file.mode); + } + + return file; +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/params.h b/loader/tools/subsizer-0.7pre1/src/params.h new file mode 100644 index 0000000..f9667ee --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/params.h @@ -0,0 +1,21 @@ +/************************************************************************** + * + * FILE params.h + * Copyright (c) 2012, 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * parameter parsing + * + ******/ +#ifndef PARAMS_H +#define PARAMS_H + +#include "memory.h" + +int parse_value(char *str); +void parse_range(char *str, mem_ptr_t *low, mem_ptr_t *high); +file_t parse_filename(char *str); + +#endif /* PARAMS_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/pathfinder.c b/loader/tools/subsizer-0.7pre1/src/pathfinder.c new file mode 100644 index 0000000..eaf6b1e --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/pathfinder.c @@ -0,0 +1,327 @@ +/************************************************************************** + * + * FILE pathfinder.c + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * find optimized path by considering encoding cost + * + ******/ +#include +#include +#include + +#include "bitfunc.h" +#include "pathfinder.h" +#include "global.h" +#include "match.h" +#include "message.h" +#include "utils.h" + +#define DEBUG_COMPLEXITY 0 + + +PrimaryPath *create_primarypath(int n, uint8_t *buf) +{ + PrimaryPath *pp; + + pp = safe_malloc(sizeof(PrimaryPath), "primarypath"); + /* allocate n+1 entries to allow for the end marker */ + pp->path = safe_malloc( (n + 1) * sizeof(Match), "matches"); + pp->n = n; + pp->buf = buf; + + /* insert end marker */ + make_end(&(pp->path[n])); + + return pp; +} + +void destroy_primarypath(PrimaryPath *pp) +{ + if (pp) { + free(pp->path); + free(pp); + } +} + + +/************************************************************************** + * + * SECTION fast cost functions + * + ******/ +double litcost[0x10000]; +double lencost[0x10000]; +double offscost1[0x10000]; +double offscost2[0x10000]; +double offscost3[0x10000]; +double offscost[0x10000]; + +static void prepare_fast(CostFuncSet *cfs, EncodingSet *es) +{ + int i; + + /* prepare tables for fast cost calculations */ + for (i = 0; i < 0x10000; i++) { + litcost[i] = cfs->cost_lit(es, i); + lencost[i] = cfs->cost_mlen(es, i); + offscost1[i] = cfs->cost_moffs(es, i, 1); + offscost2[i] = cfs->cost_moffs(es, i, 2); + offscost3[i] = cfs->cost_moffs(es, i, 3); + offscost[i] = cfs->cost_moffs(es, i, 4); + } +} + +static inline double fast_lit(int l) +{ + return litcost[l]; +} + +static inline double fast_mlen(int l) +{ + return lencost[l]; +} + +static inline double fast_moffs(int of, int l) +{ + switch (l) { + case 1: + return offscost1[of]; + case 2: + return offscost2[of]; + case 3: + return offscost3[of]; + default: + return offscost[of]; + } +} + + +/************************************************************************** + * + * NAME find_cheapest_path + * + * DESCRIPTION + * Consider encoding cost to find the cheapest path through all matches + * + ******/ +PrimaryPath *find_cheapest_path(MatchTree *mt, CostFuncSet *cfs, EncodingSet *es, unsigned int flags) +{ + int i, j; + int cur; + int len = mt->len; + double *dist; + double *prev; + Match *path; + + /* configuration */ + int min_match = 1; + int enforce_exclusion = (flags & FCP_ENFORCE_EXCLUSION); + int literal_sequences = (flags & FCP_LITERAL_SEQUENCES); + int initial_literal = (flags & FCP_INITIAL_LITERAL); + + /* construct lookup tables */ + prepare_fast(cfs, es); + + /* create path tables */ + dist = safe_malloc(sizeof(double) * (len + 1), "dist table"); + prev = safe_malloc(sizeof(double) * (len + 1), "prev table"); + path = safe_malloc(sizeof(Match) * (len + 1), "match table"); + + /* initialize costs */ + for (i = 0; i < len+1; i++) { + /* make sure we have headroom for a few additions. */ + dist[i] = INT_MAX - 0x10000; + prev[i] = -1; + //path[i] = 0; + } + dist[0] = 0; + +#if DEBUG_COMPLEXITY + int n_match, n_match_taken, n_rle, n_rle_taken, n_lt, n_lt_taken; + n_match = 0; + n_match_taken = 0; + n_rle = 0; + n_rle_taken = 0; + n_lt = 0; + n_lt_taken = 0; +#endif + + /* + * calculate costs + */ + cur = 0; + while (cur < len) { + int v; + double w; + Match *m = mt->match[cur]; + + while (m && !is_end(m)) { + + if (is_match(m) && get_match_len(m) >= min_match ) { + /* match */ + int l = get_match_len(m); + int of = get_match_offs(m); + /* + * - does not consider when the escape bit isn't needed. + */ + /* + * scan through all possible shorter lengths and see if + * any are cheaper. + * TODO: which choices are actually interesting here? + */ + int c = 4; + while (c && l >= min_match) { + w = 1 + fast_mlen(l) + fast_moffs(of, l); + v = cur + l; + +#if DEBUG_COMPLEXITY + n_match++; +#endif + if (dist[v] > dist[cur] + w) { + dist[v] = dist[cur] + w; + prev[v] = cur; + make_match(&path[v], of, l); +#if DEBUG_COMPLEXITY + n_match_taken++; +#endif + } + + l--; + c--; + } + } else if (is_rle(m) && get_rle_len(m) >= min_match) { + /* match */ + int l = get_rle_len(m); + int of = 1; + /* + * - does not consider when the escape bit isn't needed. + */ + /* + * scan through all possible shorter lengths and see if + * any are cheaper. + * TODO: which choices are actually interesting here? + */ + int c = 4; + while (c && l >= min_match) { + w = 1 + fast_mlen(l) + fast_moffs(of, l); + v = cur + l; + +#if DEBUG_COMPLEXITY + n_rle++; +#endif + if (dist[v] > dist[cur] + w) { + dist[v] = dist[cur] + w; + prev[v] = cur; + make_match(&path[v], of, l); +#if DEBUG_COMPLEXITY + n_rle_taken++; +#endif + } + + l--; + c--; + } + } + + m++; + } + + /* literal */ + /* + * - should be expanded to handle all possible literal + * sequences + */ + if ( enforce_exclusion && + (cur > 0) && (mt->buf[cur-1] == mt->buf[cur]) ) { + /* force unbearable cost for bytes breaking the + exclusion property */ + w = 30000; + } else { + w = fast_lit(1); + } + + v = cur + 1; +#if DEBUG_COMPLEXITY + n_lt++; +#endif + if (dist[v] > dist[cur] + w) { + dist[v] = dist[cur] + w; + prev[v] = cur; + make_literal(&path[v], cur, 1); +#if DEBUG_COMPLEXITY + n_lt_taken++; +#endif + } + + if ( !( initial_literal && (cur == 0) ) && literal_sequences ) { + int l = (cur + 256 < len) ? 256 : len - cur; + while (l > 1) { + w = fast_lit(l); + v = cur + l; +#if DEBUG_COMPLEXITY + n_lt++; +#endif + if (dist[v] > dist[cur] + w) { + dist[v] = dist[cur] + w; + prev[v] = cur; + make_literal(&path[v], cur, l); +#if DEBUG_COMPLEXITY + n_lt_taken++; +#endif + } + + l--; + } + } + + + cur++; + } + + msg(MSG_DEBUG, "cost=%f bits (%f bytes)\n", dist[len], (dist[len]+7)/8); + +#if DEBUG_COMPLEXITY + printf("n_match=%d (%d), n_rle=%d (%d), n_lit=%d (%d)\n", n_match, n_match_taken, n_rle, n_rle_taken, n_lt, n_lt_taken); +#endif + + /* + * Backtrack the cheapest path to find the number of primary units + * to allocate. + */ + i = len; + j = 0; + while (i > 0) { + j++; + i = prev[i]; + } + + PrimaryPath *pp; + pp = create_primarypath(j, mt->buf); + pp->len = len; + pp->cost = dist[len]; + + /* + * Backtrack the cheapest path and create primary units accordingly. + */ + i = len; + j = pp->n-1; + while (i > 0) { + pp->path[j] = path[i]; + j--; + i = prev[i]; + } + + + /* free up path tables */ + free(dist); + free(prev); + free(path); + + return pp; +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/pathfinder.h b/loader/tools/subsizer-0.7pre1/src/pathfinder.h new file mode 100644 index 0000000..60ef8e6 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/pathfinder.h @@ -0,0 +1,47 @@ +/************************************************************************** + * + * FILE pathfinder.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * create encoding structure + * + ******/ +#ifndef PATHFINDER_H +#define PATHFINDER_H + +#include +#include + +#include "match.h" + +typedef struct { + int n; + double cost; + Match *path; + uint8_t *buf; + size_t len; +} PrimaryPath; + +PrimaryPath *create_primarypath(int n, uint8_t *buf); +void destroy_primarypath(PrimaryPath *pp); + + +typedef struct EncodingSet EncodingSet; + +typedef struct { + double (*cost_lit)(EncodingSet *es, int l); + double (*cost_mlen)(EncodingSet *es, int l); + double (*cost_moffs)(EncodingSet *es, int of, int l); +} CostFuncSet; + +#define FCP_ENFORCE_EXCLUSION (1 << 0) +#define FCP_LITERAL_SEQUENCES (1 << 1) +#define FCP_INITIAL_LITERAL (1 << 2) + +PrimaryPath *find_cheapest_path(MatchTree *mt, CostFuncSet *cfs, EncodingSet *es, unsigned int flags); + + +#endif /* PATHFINDER_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/Makefile b/loader/tools/subsizer-0.7pre1/src/sfx/Makefile new file mode 100644 index 0000000..bb6f6ed --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/Makefile @@ -0,0 +1,22 @@ +# Makefile + +# top level targets +all: sfx.o + +# source files +SRC = detect_start.c generate_sfx.c fold.c + +# targets +sfx.o: $(SRC:%.c=%.o) + $(LD) -r $^ -o $@ +# clean +clean: + rm -f *~ \#*\# + rm -f *.o + rm -f *.d + rm -f a.out + +# handle dependencies +-include $(SRC:%.c=%.d) + +# eof diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal.asm b/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal.asm new file mode 100644 index 0000000..5c4995b --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal.asm @@ -0,0 +1,450 @@ +;************************************************************************** +;* +;* FILE decrunch_normal.asm +;* Copyright (c) 2015 Daniel Kahlin +;* Written by Daniel Kahlin +;* +;* DESCRIPTION +;* subsizer 0.7pre1 executable decruncher +;* +;****** + processor 6502 + + seg code + + mac NEW_PART + org $0000 + dc.b {1},0 + endm + +;************************************************************************** +;* +;* Configuration options +;* +;****** + ifnconst MAKE_EXE +HAVE_CLI equ 1 + endif +HAVE_LONG_PARTS equ 1 + + + if HAVE_LONG_PARTS +PART_MASK equ %00001111 +N_PARTS equ 16 + else +PART_MASK equ %00000111 +N_PARTS equ 8 + endif + + + NEW_PART "header" + subroutine header + org $0801 +begin_header: +;************************************************************************** +;* +;* Basic line! +;* +;****** + if 0 +start_of_line: + dc.w end_line + dc.w 0 + dc.b $9e,"2069 /T.L.R/",0 +end_line: + dc.w 0 + else +start_of_line: + dc.w end_line + dc.w 0 + dc.b $9e,"2063 SUBSIZER!",0 +end_line: + dc.b $a0,$00 + endif + +;************************************************************************** +;* +;* NAME start +;* +;****** +start: + ifnconst MAKE_EXE + sei + lda #$34 + sta $01 + jmp tail + endif + +end_header: + + + + NEW_PART "tail" + subroutine tail + org $1000 +begin_tail: +;************************************************************************** +;* +;* NAME tail +;* +;****** +tail: + ifnconst MAKE_EXE +wrap equ $07e8 +wrap_st equ $3000 +WRAP_LEN equ $10 + + + ldy #DECRUNCHER_LEN +tl_lp1: + lda.w decruncher_st-1,y + sta.w decruncher-1,y + cpy #WRAP_LEN+1 + bcs tl_skp1 + lda.w wrap_st-1,y + sta.w wrap-1,y +tl_skp1: + dey + bne tl_lp1 +; Y = 0 + endif + +; ldy #0 +dc_lp01: + ldx #4 + jsr dc_get_bits + sta bits,y + + tya + and #PART_MASK + bne dc_skp01 +; Acc = 0 + sta base_l,y + sta base_h,y + beq dc_skp02 ; always taken +dc_skp01: + lda #0 + sta.z tmp_zp + ldx bits-1,y + sec +dc_lp02: + rol + rol.z tmp_zp + dex + bpl dc_lp02 +; C = 0 +; clc + adc base_l-1,y + sta base_l,y + lda.z tmp_zp + adc base_h-1,y + sta base_h,y +dc_skp02: + iny + cpy #N_PARTS*4+4 + bne dc_lp01 + +; perform decrunch + ldy #0 + jmp decrunch_entry + +end_tail: + + + NEW_PART "decruncher" + org zp_end + subroutine decruncher +begin_decruncher: +;************************************************************************** +;* +;* NAME decruncher +;* +;* DESCRIPTION +;* decruncher +;* +;****** + seg.u zp + org $f9 +tmp_zp: +len_zp: + ds.b 1 +copy_zp: + ds.w 1 +hibits_zp: + ds.b 1 +zp_end: + seg code +decruncher_st: + rorg zp_end +decruncher: ;+0ftDecruncher + +dest_zp: + dc.w $0000 ;+0ftDestEnd +buf_zp: + dc.b $80 ;+0ftBufZp + + + if HAVE_LONG_PARTS +tabo: + dc.b 48,0,16,32 +tabb: + dc.b 2,4,4,4 + else +tabo: + dc.b 24,0,8,16 +tabb: + dc.b 2,3,3,3 + endif + +decrunch_entry: +dc_literal: + lda dest_zp + bne dc_skp5 + dec dest_zp+1 +dc_skp5: + dec dest_zp + jsr dc_get_byte +; ldy #0 + sta (dest_zp),y +; bcs dc_lp1 ; always taken + +decrunch_main: +; Y = 0 +;------ +; perform actual decrunch +dc_lp1: + jsr dc_get_bit + bcs dc_literal +; Y = 0 +; get length as bits/base. +; ldy #$ff+1 +dc_lp2: + iny + cpy #N_PARTS + beq dc_skp0 + jsr dc_get_bit + bcc dc_lp2 +dc_skp0: + ldx bits_len-1,y + jsr dc_get_bits +; C = 0 + adc base_len-1,y + sta len_zp +; C = 0 + +;****** +;* IN: len = $01..$100 (Acc = $00..$ff) +;* OUT: dest_zp = dest_zp - len, Y = len-1 +;* + tay +; clc + eor #$ff + adc dest_zp + sta dest_zp + bcs dc_skp22 + dec dest_zp+1 +dc_skp22: + +; check end marker here to avoid thrashing carry earlier + cpy #$00 ;+1ftEndMarkerMinusOne + beq done + +;****** +;* Get selector bits depending on length. +;* +;* IN: len = $01..$100 (Y = $00..$ff) +;* OUT: +;* + cpy #4 + bcc dc_skp2 + ldy #3 +dc_skp2: + +; get offset as bits/base. + ldx tabb,y + jsr dc_get_bits +; C = 0 + adc tabo,y + tay + + ldx bits_offs,y + jsr dc_get_bits +; C = 0 + adc base_offs_l,y + tax + lda hibits_zp + adc base_offs_h,y + tay +; X/Y = offset - 1 + + sec + txa + adc dest_zp + sta copy_zp + tya + adc dest_zp+1 + sta copy_zp+1 + +;****** +;* Reverse fast copy +;* +;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0 +;* + ldy len_zp + beq dc_skp4 +dc_lp4: + lda (copy_zp),y + sta (dest_zp),y + dey + bne dc_lp4 +dc_skp4: + lda (copy_zp),y + sta (dest_zp),y + bcc dc_lp1 ; always taken + + +;************************************************************************** +;* +;* NAME dc_get_bits +;* +;* DESCRIPTION +;* Get bits from the packed stream. +;* +;* IN: +;* X = number of bits to get +;* +;* OUT: +;* Acc = bit 7-0 +;* hibits_zp = bit 15-8 +;* C = bit 16 +;* Y = preserved +;* X = 0 +;* Z = 1 +;* +;****** +dc_get_bits: + lda #0 + sta hibits_zp + cpx #1 + bcc dcg_ex1 +dcg_lp1: + asl buf_zp + bne dcg_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta buf_zp + pla +dcg_skp1: + rol + rol hibits_zp + dex + bne dcg_lp1 ; C=0 for all X!=0 +dcg_ex1: + rts + + +;************************************************************************** +;* +;* NAME dc_get_bit +;* +;* DESCRIPTION +;* Get one bit from the packed stream into carry +;* +;* IN: +;* - +;* +;* OUT: +;* Acc = ? +;* C = bit 0 +;* Y = preserved +;* X = preserved +;* Z = 0 +;* +;****** +dc_get_bit: + asl buf_zp + bne dcgb_ex1 +; C=1 (because the marker bit was just shifted out) + jsr dc_get_byte + rol + sta buf_zp +dcgb_ex1: + rts + + +;************************************************************************** +;* +;* NAME dc_get_byte +;* +;* DESCRIPTION +;* Get byte from the packed stream. +;* +;****** +dc_get_byte: + lda dc_ptr + bne dcgb_skp1 + dec dc_ptr+1 +dcgb_skp1: + dec dc_ptr +dc_ptr equ . + 1 + lda.w $0000 ;+1ftSrcEnd + rts + +;****** +;* exit out +done: + ifnconst MAKE_EXE + lda #$37 + sta $01 + if HAVE_CLI + cli + endif + jmp $0830 + endif + + rend +DECRUNCHER_LEN equ . - decruncher_st + + +end_decruncher: + + seg.u tables + org $0334 +begin_tables: +;************************************************************************** +;* +;* NAME base_l, base_h, bits +;* +;* DESCRIPTION +;* Data for bits/base decoding. +;* +;****** +base_l: +base_len: + ds.b N_PARTS,0 +base_offs_l: + ds.b N_PARTS*3+4,0 +base_h equ . - N_PARTS +; ds.b N_PARTS,0 +base_offs_h: + ds.b N_PARTS*3+4,0 + +bits: +bits_len: + ds.b N_PARTS,0 +bits_offs: + ds.b N_PARTS*3+4,0 + +end_tables: + + + ifnconst MAKE_EXE + echo "header", begin_header, end_header, end_header-begin_header + echo "tail", begin_tail, end_tail, end_tail-begin_tail + echo "decruncher", begin_decruncher, end_decruncher, end_decruncher-begin_decruncher + echo "[run] decruncher", decruncher, decruncher+DECRUNCHER_LEN, DECRUNCHER_LEN + echo "[run] tables", begin_tables, end_tables, end_tables-begin_tables + endif +; eof diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal_dirty.asm b/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal_dirty.asm new file mode 100644 index 0000000..6d68504 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/decrunch_normal_dirty.asm @@ -0,0 +1,474 @@ +;************************************************************************** +;* +;* FILE decrunch_normal_dirty.asm +;* Copyright (c) 2015, 2017 Daniel Kahlin +;* Written by Daniel Kahlin +;* +;* DESCRIPTION +;* subsizer 0.7pre1 executable decruncher - dirty version +;* +;****** + processor 6502 + + seg code + + mac NEW_PART + org $0000 + dc.b {1},0 + endm + +;************************************************************************** +;* +;* Configuration options +;* +;****** + ifnconst MAKE_EXE +HAVE_CLI equ 1 + endif +HAVE_LONG_PARTS equ 1 + + + if HAVE_LONG_PARTS +PART_MASK equ %00001111 +N_PARTS equ 16 + else +PART_MASK equ %00000111 +N_PARTS equ 8 + endif + + + NEW_PART "header" + subroutine header + org $0801 +begin_header: +;************************************************************************** +;* +;* Basic line! +;* +;****** + if 0 +start_of_line: + dc.w end_line + dc.w 0 + dc.b $9e,"2069 /T.L.R/",0 +end_line: + dc.w 0 + else +start_of_line: + dc.w end_line + dc.w 0 + dc.b $9e,"2063 SUBSIZER!",0 +end_line: + dc.b $a0,$00 + endif + +;************************************************************************** +;* +;* NAME start +;* +;****** +start: + ifnconst MAKE_EXE + sei + lda #$34 + sta $01 + jmp tail + endif + +end_header: + + + + NEW_PART "tail" + subroutine tail + org $1000 +begin_tail: +;************************************************************************** +;* +;* NAME tail +;* +;****** +tail: + ifnconst MAKE_EXE +wrap equ $07e8 +wrap_st equ $3000 +WRAP_LEN equ $10 + + + ldx #DECRUNCHER_LEN +tl_lp1: + lda.w decruncher_st-1,x + sta.w decruncher-1,x + cpx #WRAP_LEN+1 + bcs tl_skp1 + lda.w wrap_st-1,x + sta.w wrap-1,x +tl_skp1: + dex + bne tl_lp1 +; X = 0 + endif + +; ldx #0 +dc_lp01: + +;****** +;* get 4 bits +; could be optimized by storing the bits in zp from the beginning and +; then shifting out 4 bits at a time, increasing the ptr. + lda #%11100000 +dcg_lp1: + asl.z buf_zp + bne dcg_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta.z buf_zp + tya +dcg_skp1: + rol + bcs dcg_lp1 +; Acc = 4 bits. + sta.z bits,x + + txa + and #PART_MASK + tay + beq dc_skp01 + + lda #0 + sta.z hibits_zp + ldy.z bits-1,x + sec +dc_lp02: + rol + rol.z hibits_zp + dey + bpl dc_lp02 +; C = 0 +; clc + adc.z base_l-1,x + tay + lda.z hibits_zp + adc.z base_h-1,x + +dc_skp01: + sta.z base_h,x + sty.z base_l,x + + inx + cpx #N_PARTS*4+4 + bne dc_lp01 + +; perform decrunch + ldy #0 + jmp decrunch_entry + + + +end_tail: + + + NEW_PART "decruncher" + org zp_end + subroutine decruncher +begin_decruncher: +;************************************************************************** +;* +;* NAME decruncher +;* +;* DESCRIPTION +;* decruncher +;* +;****** + seg.u zp + org $be +hibits_zp: + ds.b 1 +zp_end: + seg code +decruncher_st: + rorg zp_end +decruncher: ;+0ftDecruncher + +buf_zp: + dc.b $80 ;+0ftBufZp + + if HAVE_LONG_PARTS +tabb: + dc.b %10000000 | [48 >> 2] ; 2 bits + dc.b %11100000 | [0 >> 4] ; 4 bits + dc.b %11100000 | [16 >> 4] ; 4 bits + dc.b %11100000 | [32 >> 4] ; 4 bits + else +tabb: + dc.b %10000000 | [24 >> 2] ; 2 bits + dc.b %11000000 | [0 >> 3] ; 3 bits + dc.b %11000000 | [8 >> 3] ; 3 bits + dc.b %11000000 | [16 >> 3] ; 3 bits + endif + +;****** +;* get bit macro + mac get_bit + asl.z buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + jsr dc_get_byte + rol + sta.z buf_zp +.gb_skp1: + endm + +;****** +;* get bits max8 macro + mac get_bits_max8 +.gb_lp1: + asl.z buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta.z buf_zp + pla +.gb_skp1: + rol + dey + bne .gb_lp1 + endm + + +;****** +;* get bits max8 masked macro + mac get_bits_max8_masked +.gb_lp1: + asl.z buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta.z buf_zp + tya +.gb_skp1: + rol + bcs .gb_lp1 + endm + + +;****** +;* get bits max16 macro + mac get_bits_max16 +.gb_lp1: + asl.z buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta.z buf_zp + pla +.gb_skp1: + rol + rol.z hibits_zp + dey + bne .gb_lp1 ; C=0 for all Y!=0 + endm + + +;************************************************************************** +;* +;* NAME dc_get_byte +;* +;* DESCRIPTION +;* Get byte from the packed stream. +;* +;****** +dc_get_byte: + lda.z dc_ptr + bne dcgb_skp1 + dec.z dc_ptr+1 +dcgb_skp1: + dec.z dc_ptr +dc_ptr equ . + 1 + lda.w $0000 ;+1ftSrcEnd + rts + + +;****** +;* Reverse fast copy +;* +;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0 +;* +copy: +len_zp equ . + 1 + ldy #0 + beq dc_skp4 +dc_lp4: +copy_zp equ .+1 + lda.w $0000,y +dest_zp equ .+1 + sta.w $0000,y ;+1ftDestEnd + dey + bne dc_lp4 +dc_skp4: + lda (copy_zp),y +; sta (dest_zp),y + bcc dc_common ; always taken + +decrunch_entry: +dc_literal: + lda dest_zp + bne dc_skp5 + dec dest_zp+1 +dc_skp5: + dec dest_zp + jsr dc_get_byte +; ldy #0 +dc_common: + sta (dest_zp),y + ; fall through + +decrunch_main: +;------ +; perform actual decrunch +dc_lp1: + get_bit + bcs dc_literal + +; get length as bits/base. + ldx #$100-N_PARTS +dc_lp2: + inx + beq dc_skp0 + get_bit + bcc dc_lp2 + clc +dc_skp0: +; C = 0, Y = 0 +; lda #0 + tya + ldy.z [bits_len+N_PARTS-1]&$ff,x + beq dcb1_skp2 + get_bits_max8 +dcb1_skp2: +; C = 0 + adc.z [base_len+N_PARTS-1]&$ff,x + sta len_zp +; C = 0 + +;****** +;* IN: len = $01..$100 (Acc = $00..$ff) +;* OUT: dest_zp = dest_zp - len, X = len-1 +;* + tax +; clc + eor #$ff + adc dest_zp + sta dest_zp + bcs dc_skp22 + dec dest_zp+1 +dc_skp22: + +; check end marker here to avoid thrashing carry earlier + cpx #$00 ;+1ftEndMarkerMinusOne + beq done + +;****** +;* Get selector bits depending on length. +;* +;* IN: len = $01..$100 (X = $00..$ff) +;* OUT: +;* + cpx #4 + bcc dc_skp2 + ldx #3 +dc_skp2: + +; get offset as bits/base. + lda tabb,x + get_bits_max8_masked + tax +; C = 0 + + lda #0 + sta hibits_zp + ldy.z bits_offs,x + beq dcb3_skp2 + get_bits_max16 +dcb3_skp2: +; C = 0, Acc/hibits_zp + base_offs,x = offset - 1 + +; perform: copy_zp = Acc/hibits_zp + base_offs,x + 1 + dest_zp +; result: copy_zp = dest_zp + offset + adc.z base_offs_l,x + bcc dcb3_skp3 + inc hibits_zp +dcb3_skp3: + sec + adc dest_zp + sta copy_zp + lda hibits_zp + adc.z base_offs_h,x +; C = 0 + adc dest_zp+1 + sta copy_zp+1 + + jmp copy + +done: + ifnconst MAKE_EXE + lda #$37 + sta $01 + if HAVE_CLI + cli + endif + jmp $0830 + endif + + rend +DECRUNCHER_LEN equ . - decruncher_st + + +end_decruncher: + + seg.u tables + org $0002 +begin_tables: +;************************************************************************** +;* +;* NAME base_l, base_h, bits +;* +;* DESCRIPTION +;* Data for bits/base decoding. +;* +;****** +base_l: +base_len: + ds.b N_PARTS,0 +base_offs_l: + ds.b N_PARTS*3+4,0 +base_h equ . - N_PARTS +; ds.b N_PARTS,0 +base_offs_h: + ds.b N_PARTS*3+4,0 + +bits: +bits_len: + ds.b N_PARTS,0 +bits_offs: + ds.b N_PARTS*3+4,0 + +end_tables: + + + ifnconst MAKE_EXE + echo "header", begin_header, end_header, end_header-begin_header + echo "tail", begin_tail, end_tail, end_tail-begin_tail + echo "decruncher", begin_decruncher, end_decruncher, end_decruncher-begin_decruncher + echo "[run] decruncher", decruncher, decruncher+DECRUNCHER_LEN, DECRUNCHER_LEN + echo "[run] tables", begin_tables, end_tables, end_tables-begin_tables + endif +; eof diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers.h b/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers.h new file mode 100644 index 0000000..d50d397 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers.h @@ -0,0 +1,37 @@ +/* autogenerated by make_exe.pl, do not edit */ +#ifndef DECRUNCHERS_H +#define DECRUNCHERS_H + +#include + +enum fixtype_t { + ftBufZp = 1, + ftDecruncher, + ftDestEnd, + ftEndMarkerMinusOne, + ftSrcEnd, + ftEnd = -1 +}; + +typedef struct { + enum fixtype_t type; + uint16_t addr; +} FixEntry; + +#define FLAG_DIRTY (1<<0) +#define FLAG_NOCLI (1<<1) +#define FLAG_MASK (FLAG_DIRTY | FLAG_NOCLI) + +#define FLAG_XBASE (1<<2) + +typedef struct { + uint16_t addr; + uint8_t *data; + int len; + FixEntry *fix_entry; + int flags; +} FixStruct; + + +#endif /* DECRUNCHERS_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers_data.h b/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers_data.h new file mode 100644 index 0000000..36817cd --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/decrunchers_data.h @@ -0,0 +1,259 @@ +/* autogenerated by make_exe.pl, do not edit */ +#include +#include "decrunchers.h" + +#define XOR_MAGIC 0xa7 + +static uint8_t default_header[] = { + 0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91, + 0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd, + 0xe2,0xf5,0x86,0xa7,0x07,0xa7 +}; +static uint8_t default_tail[] = { + 0x05,0xa3,0x87,0xdd,0xa6,0x3e,0x0b,0xa4, + 0x3f,0x8e,0xa8,0x77,0xaf,0x3e,0x93,0xa4, + 0x3e,0xcf,0xa4,0x57,0xbb,0x0e,0xa7,0x22, + 0x5e,0x19,0x0c,0xa4,0x9f,0x8d,0x81,0x5e, + 0x6d,0xb7,0x5d,0xde,0x94,0xa4,0x3e,0x93, + 0xa4,0x02,0x5e,0xde,0xc0,0xa4,0x3e,0xcf, + 0xa4,0x6f,0x67,0xe3,0x77,0x6d,0x07,0xa7, + 0xeb,0xaf,0xa6 +}; +static uint8_t default_decruncher[] = { + 0xa7,0xa7,0x27,0x97,0xa7,0xb7,0x87,0xa5, + 0xa3,0xa3,0xa3,0x02,0x5a,0x77,0xa5,0x61, + 0x59,0x61,0x5a,0x87,0x07,0xa6,0x36,0x5a, + 0x87,0x32,0xa6,0x17,0x49,0x6f,0x67,0xb7, + 0x57,0xa2,0x87,0x32,0xa6,0x37,0x51,0x19, + 0x0c,0xa4,0x87,0xdd,0xa6,0xde,0x94,0xa4, + 0x22,0x5e,0x0f,0xee,0x58,0xc2,0x5a,0x22, + 0x5a,0x17,0xa5,0x61,0x59,0x67,0xa7,0x57, + 0xd6,0x67,0xa3,0x37,0xa5,0x07,0xa4,0x19, + 0xa3,0xa6,0x87,0xdd,0xa6,0xde,0xa7,0xa6, + 0x0f,0x19,0x1b,0xa4,0x87,0xdd,0xa6,0xde, + 0xe3,0xa4,0x0d,0x02,0x5b,0xde,0xdf,0xa4, + 0x0f,0x9f,0x2d,0xc2,0x5a,0x22,0x5d,0x3f, + 0xc2,0x59,0x22,0x5c,0x03,0x5e,0x57,0xa0, + 0x16,0x5d,0x36,0x5a,0x2f,0x77,0x5e,0x16, + 0x5d,0x36,0x5a,0x37,0x3c,0x0e,0xa7,0x22, + 0x5b,0x47,0xa6,0x37,0xb5,0xa1,0x58,0x77, + 0xaf,0xef,0x87,0x07,0xa6,0x8d,0x22,0x58, + 0xcf,0x8d,0x81,0x5b,0x6d,0x77,0x49,0xc7, + 0xa1,0x58,0x77,0xa1,0x87,0x07,0xa6,0x8d, + 0x22,0x58,0xc7,0x0a,0x0b,0xa6,0x77,0xa4, + 0x69,0x0a,0xa6,0x69,0x0b,0xa6,0x0a,0xa7, + 0xa7,0xc7 +}; +static FixEntry ft_default_header[] = { + {ftEnd, 0} +}; + +static FixEntry ft_default_tail[] = { + {ftEnd, 0} +}; + +static FixEntry ft_default_decruncher[] = { + {ftDecruncher, 0x00fd}, + {ftDestEnd, 0x00fd}, + {ftBufZp, 0x00ff}, + {ftEndMarkerMinusOne, 0x013b}, + {ftSrcEnd, 0x01ac}, + {ftEnd, 0} +}; + +static uint8_t nocli_header[] = { + 0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91, + 0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd, + 0xe2,0xf5,0x86,0xa7,0x07,0xa7 +}; +static uint8_t nocli_tail[] = { + 0x05,0xa3,0x87,0xdd,0xa6,0x3e,0x0b,0xa4, + 0x3f,0x8e,0xa8,0x77,0xaf,0x3e,0x93,0xa4, + 0x3e,0xcf,0xa4,0x57,0xbb,0x0e,0xa7,0x22, + 0x5e,0x19,0x0c,0xa4,0x9f,0x8d,0x81,0x5e, + 0x6d,0xb7,0x5d,0xde,0x94,0xa4,0x3e,0x93, + 0xa4,0x02,0x5e,0xde,0xc0,0xa4,0x3e,0xcf, + 0xa4,0x6f,0x67,0xe3,0x77,0x6d,0x07,0xa7, + 0xeb,0xaf,0xa6 +}; +static uint8_t nocli_decruncher[] = { + 0xa7,0xa7,0x27,0x97,0xa7,0xb7,0x87,0xa5, + 0xa3,0xa3,0xa3,0x02,0x5a,0x77,0xa5,0x61, + 0x59,0x61,0x5a,0x87,0x07,0xa6,0x36,0x5a, + 0x87,0x32,0xa6,0x17,0x49,0x6f,0x67,0xb7, + 0x57,0xa2,0x87,0x32,0xa6,0x37,0x51,0x19, + 0x0c,0xa4,0x87,0xdd,0xa6,0xde,0x94,0xa4, + 0x22,0x5e,0x0f,0xee,0x58,0xc2,0x5a,0x22, + 0x5a,0x17,0xa5,0x61,0x59,0x67,0xa7,0x57, + 0xd6,0x67,0xa3,0x37,0xa5,0x07,0xa4,0x19, + 0xa3,0xa6,0x87,0xdd,0xa6,0xde,0xa7,0xa6, + 0x0f,0x19,0x1b,0xa4,0x87,0xdd,0xa6,0xde, + 0xe3,0xa4,0x0d,0x02,0x5b,0xde,0xdf,0xa4, + 0x0f,0x9f,0x2d,0xc2,0x5a,0x22,0x5d,0x3f, + 0xc2,0x59,0x22,0x5c,0x03,0x5e,0x57,0xa0, + 0x16,0x5d,0x36,0x5a,0x2f,0x77,0x5e,0x16, + 0x5d,0x36,0x5a,0x37,0x3c,0x0e,0xa7,0x22, + 0x5b,0x47,0xa6,0x37,0xb5,0xa1,0x58,0x77, + 0xaf,0xef,0x87,0x07,0xa6,0x8d,0x22,0x58, + 0xcf,0x8d,0x81,0x5b,0x6d,0x77,0x49,0xc7, + 0xa1,0x58,0x77,0xa1,0x87,0x07,0xa6,0x8d, + 0x22,0x58,0xc7,0x0a,0x0b,0xa6,0x77,0xa4, + 0x69,0x0a,0xa6,0x69,0x0b,0xa6,0x0a,0xa7, + 0xa7,0xc7 +}; +static FixEntry ft_nocli_header[] = { + {ftEnd, 0} +}; + +static FixEntry ft_nocli_tail[] = { + {ftEnd, 0} +}; + +static FixEntry ft_nocli_decruncher[] = { + {ftDecruncher, 0x00fd}, + {ftDestEnd, 0x00fd}, + {ftBufZp, 0x00ff}, + {ftEndMarkerMinusOne, 0x013b}, + {ftSrcEnd, 0x01ac}, + {ftEnd, 0} +}; + +static uint8_t dirty_default_header[] = { + 0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91, + 0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd, + 0xe2,0xf5,0x86,0xa7,0x07,0xa7 +}; +static uint8_t dirty_default_tail[] = { + 0x0e,0x47,0xa1,0x18,0x77,0xaf,0x0f,0x87, + 0x63,0xa7,0x8d,0x22,0x18,0x3f,0x8d,0x17, + 0x56,0x32,0xdd,0x2d,0x8e,0xa8,0x0f,0x57, + 0xb3,0x0e,0xa7,0x22,0x19,0x13,0xde,0x9f, + 0x8d,0x81,0x19,0x2f,0xb7,0x5d,0xd2,0xa6, + 0x0f,0x02,0x19,0xd2,0x92,0x32,0x91,0x33, + 0xa5,0x4f,0x47,0xe3,0x77,0x6d,0x07,0xa7, + 0xeb,0x46,0xa7 +}; +static uint8_t dirty_default_decruncher[] = { + 0x27,0x2b,0x47,0x46,0x45,0x02,0x6a,0x77, + 0xa5,0x61,0x69,0x61,0x6a,0x0a,0xa7,0xa7, + 0xc7,0x07,0xa7,0x57,0xae,0x1e,0xa7,0xa7, + 0x3e,0xa7,0xa7,0x2f,0x77,0x50,0x16,0x72, + 0x37,0xac,0x02,0x7f,0x77,0xa5,0x61,0x7e, + 0x61,0x7f,0x87,0x63,0xa7,0x36,0x7f,0xa1, + 0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,0x22, + 0x18,0x17,0x40,0x05,0x57,0x4f,0x57,0xaa, + 0xa1,0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d, + 0x22,0x18,0x37,0x56,0xbf,0x3f,0x13,0x2e, + 0x57,0xb7,0xa1,0x18,0x77,0xaf,0xef,0x87, + 0x63,0xa7,0x8d,0x22,0x18,0xcf,0x8d,0x2f, + 0x77,0x57,0xd2,0xb6,0x22,0x76,0x0d,0xee, + 0x58,0xc2,0x7f,0x22,0x7f,0x17,0xa5,0x61, + 0x7e,0x47,0xa7,0x57,0xef,0x47,0xa3,0x37, + 0xa5,0x05,0xa4,0x12,0x67,0xa1,0x18,0x77, + 0xaf,0x0f,0x87,0x63,0xa7,0x8d,0x22,0x18, + 0x3f,0x8d,0x17,0x56,0x0d,0x0e,0xa7,0x22, + 0x19,0x13,0x2d,0x57,0xb5,0xa1,0x18,0x77, + 0xaf,0xef,0x87,0x63,0xa7,0x8d,0x22,0x18, + 0xcf,0x8d,0x81,0x19,0x2f,0x77,0x49,0xd2, + 0xb5,0x37,0xa5,0x41,0x19,0x9f,0xc2,0x7f, + 0x22,0x72,0x02,0x19,0xd2,0xe1,0xc2,0x7e, + 0x22,0x71,0xeb,0x77,0xa7 +}; +static FixEntry ft_dirty_default_header[] = { + {ftEnd, 0} +}; + +static FixEntry ft_dirty_default_tail[] = { + {ftEnd, 0} +}; + +static FixEntry ft_dirty_default_decruncher[] = { + {ftDecruncher, 0x00bf}, + {ftBufZp, 0x00bf}, + {ftSrcEnd, 0x00cd}, + {ftDestEnd, 0x00d8}, + {ftEndMarkerMinusOne, 0x0131}, + {ftEnd, 0} +}; + +static uint8_t dirty_nocli_header[] = { + 0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91, + 0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd, + 0xe2,0xf5,0x86,0xa7,0x07,0xa7 +}; +static uint8_t dirty_nocli_tail[] = { + 0x0e,0x47,0xa1,0x18,0x77,0xaf,0x0f,0x87, + 0x63,0xa7,0x8d,0x22,0x18,0x3f,0x8d,0x17, + 0x56,0x32,0xdd,0x2d,0x8e,0xa8,0x0f,0x57, + 0xb3,0x0e,0xa7,0x22,0x19,0x13,0xde,0x9f, + 0x8d,0x81,0x19,0x2f,0xb7,0x5d,0xd2,0xa6, + 0x0f,0x02,0x19,0xd2,0x92,0x32,0x91,0x33, + 0xa5,0x4f,0x47,0xe3,0x77,0x6d,0x07,0xa7, + 0xeb,0x46,0xa7 +}; +static uint8_t dirty_nocli_decruncher[] = { + 0x27,0x2b,0x47,0x46,0x45,0x02,0x6a,0x77, + 0xa5,0x61,0x69,0x61,0x6a,0x0a,0xa7,0xa7, + 0xc7,0x07,0xa7,0x57,0xae,0x1e,0xa7,0xa7, + 0x3e,0xa7,0xa7,0x2f,0x77,0x50,0x16,0x72, + 0x37,0xac,0x02,0x7f,0x77,0xa5,0x61,0x7e, + 0x61,0x7f,0x87,0x63,0xa7,0x36,0x7f,0xa1, + 0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,0x22, + 0x18,0x17,0x40,0x05,0x57,0x4f,0x57,0xaa, + 0xa1,0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d, + 0x22,0x18,0x37,0x56,0xbf,0x3f,0x13,0x2e, + 0x57,0xb7,0xa1,0x18,0x77,0xaf,0xef,0x87, + 0x63,0xa7,0x8d,0x22,0x18,0xcf,0x8d,0x2f, + 0x77,0x57,0xd2,0xb6,0x22,0x76,0x0d,0xee, + 0x58,0xc2,0x7f,0x22,0x7f,0x17,0xa5,0x61, + 0x7e,0x47,0xa7,0x57,0xef,0x47,0xa3,0x37, + 0xa5,0x05,0xa4,0x12,0x67,0xa1,0x18,0x77, + 0xaf,0x0f,0x87,0x63,0xa7,0x8d,0x22,0x18, + 0x3f,0x8d,0x17,0x56,0x0d,0x0e,0xa7,0x22, + 0x19,0x13,0x2d,0x57,0xb5,0xa1,0x18,0x77, + 0xaf,0xef,0x87,0x63,0xa7,0x8d,0x22,0x18, + 0xcf,0x8d,0x81,0x19,0x2f,0x77,0x49,0xd2, + 0xb5,0x37,0xa5,0x41,0x19,0x9f,0xc2,0x7f, + 0x22,0x72,0x02,0x19,0xd2,0xe1,0xc2,0x7e, + 0x22,0x71,0xeb,0x77,0xa7 +}; +static FixEntry ft_dirty_nocli_header[] = { + {ftEnd, 0} +}; + +static FixEntry ft_dirty_nocli_tail[] = { + {ftEnd, 0} +}; + +static FixEntry ft_dirty_nocli_decruncher[] = { + {ftDecruncher, 0x00bf}, + {ftBufZp, 0x00bf}, + {ftSrcEnd, 0x00cd}, + {ftDestEnd, 0x00d8}, + {ftEndMarkerMinusOne, 0x0131}, + {ftEnd, 0} +}; + +static FixStruct fs_list_decruncher[] = { + {0x00fd, default_decruncher, sizeof(default_decruncher), ft_default_decruncher, }, + {0x00fd, nocli_decruncher, sizeof(nocli_decruncher), ft_nocli_decruncher, FLAG_NOCLI}, + {0x00bf, dirty_default_decruncher, sizeof(dirty_default_decruncher), ft_dirty_default_decruncher, FLAG_DIRTY}, + {0x00bf, dirty_nocli_decruncher, sizeof(dirty_nocli_decruncher), ft_dirty_nocli_decruncher, FLAG_NOCLI|FLAG_DIRTY}, + {0, NULL, 0, NULL, 0} +}; +static FixStruct fs_list_header[] = { + {0x0801, default_header, sizeof(default_header), ft_default_header, }, + {0x0801, nocli_header, sizeof(nocli_header), ft_nocli_header, FLAG_NOCLI}, + {0x0801, dirty_default_header, sizeof(dirty_default_header), ft_dirty_default_header, FLAG_DIRTY}, + {0x0801, dirty_nocli_header, sizeof(dirty_nocli_header), ft_dirty_nocli_header, FLAG_NOCLI|FLAG_DIRTY}, + {0, NULL, 0, NULL, 0} +}; +static FixStruct fs_list_tail[] = { + {0x1000, default_tail, sizeof(default_tail), ft_default_tail, }, + {0x1000, nocli_tail, sizeof(nocli_tail), ft_nocli_tail, FLAG_NOCLI}, + {0x1000, dirty_default_tail, sizeof(dirty_default_tail), ft_dirty_default_tail, FLAG_DIRTY}, + {0x1000, dirty_nocli_tail, sizeof(dirty_nocli_tail), ft_dirty_nocli_tail, FLAG_NOCLI|FLAG_DIRTY}, + {0, NULL, 0, NULL, 0} +}; + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.c b/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.c new file mode 100644 index 0000000..78ab209 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.c @@ -0,0 +1,44 @@ +/************************************************************************** + * + * FILE detect_start.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Try to detect start address for a program file. + * + ******/ +#include +#include +#include + +#include "../global.h" +#include "../memory.h" +#include "detect_start.h" + +/************************************************************************** + * + * SECTION memory functions + * + ******/ +int detect_start(Memory *mem) +{ + int jmp = -1; + + if (mem->low == 0x0801) { + /* try to find jmp by looking at the sys */ + if (get_byte(mem, 0x0805) == 0x9e) { + int ad; + for (ad = 0x0806; ad < 0x900; ad++) { + if (isdigit(get_byte(mem, ad))) { + break; + } + } + jmp = atoi((char *)&mem->buf[ad]); + } + } + return jmp; +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.h b/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.h new file mode 100644 index 0000000..4e8c9e3 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/detect_start.h @@ -0,0 +1,19 @@ +/************************************************************************** + * + * FILE detect_start.h + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * Try to detect start address for a program file. + * + ******/ +#ifndef DETECT_START_H +#define DETECT_START_H + +#include "../memory.h" + +int detect_start(Memory *mem); + +#endif /* DETECT_START_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/fold.c b/loader/tools/subsizer-0.7pre1/src/sfx/fold.c new file mode 100644 index 0000000..dd301d5 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/fold.c @@ -0,0 +1,362 @@ +/************************************************************************** + * + * FILE fold.c + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#include +#include +#include +#include + +#include "decrunchers.h" +#include "fold.h" +#include "generate_sfx.h" +#include "ops6502.h" +#include "../global.h" +#include "../memory.h" + + +/************************************************************************** + * + * NAME is_basic(), is_io(), is_kernal() + * + * DESCRIPTION + * Identify if address is within special section. + * + ******/ +static int is_basic(mem_ptr_t ad) +{ + if (ad >= 0xa000 && ad <= 0xbfff) { + return 1; + } + return 0; +} + +static int is_io(mem_ptr_t ad) +{ + if (ad >= 0xd000 && ad <= 0xdfff) { + return 1; + } + return 0; +} + +static int is_kernal(mem_ptr_t ad) +{ + if (ad >= 0xe000 && ad <= 0xffff) { + return 1; + } + return 0; +} + + +/************************************************************************** + * + * SECTION rle parser + * + ******/ +typedef struct { + mem_ptr_t sa; + size_t n; + uint8_t val; +} RleEntry; + +static int cmp_rle(const void *a, const void *b) +{ + const RleEntry *ar = a; + const RleEntry *br = b; + + /* sort on I/O area first */ + if (is_io(ar->sa) && !is_io(br->sa)) { + return 1; + } + if (!is_io(ar->sa) && is_io(br->sa)) { + return -1; + } + + /* sort on kernal area second */ + if (is_kernal(ar->sa) && !is_kernal(br->sa)) { + return 1; + } + if (!is_kernal(ar->sa) && is_kernal(br->sa)) { + return -1; + } + + /* sort on basic area third */ + if (is_basic(ar->sa) && !is_basic(br->sa)) { + return 1; + } + if (!is_basic(ar->sa) && is_basic(br->sa)) { + return -1; + } + + /* lastly sort on size */ + if (ar->n < br->n) { + return 1; + } + if (ar->n > br->n) { + return -1; + } + + return 0; +} + +static size_t n_entries; +static RleEntry rle[256]; + +static void scan_rle(Memory *mem, mem_ptr_t sa, mem_ptr_t ea) +{ + mem_ptr_t ad = sa; + int n_rle; + int last_byte; + int min_rle = 50; + + n_entries = 0; + n_rle = 0; + last_byte = -1; + while (ad < ea) { + uint8_t c = get_byte(mem, ad); + if (c != last_byte) { + if (n_rle >= min_rle) { + rle[n_entries].sa = ad - n_rle - 1; + rle[n_entries].n = n_rle; + rle[n_entries].val = last_byte; + n_entries++; + } + n_rle = 0; + last_byte = c; + } else { + n_rle++; + } + ad++; + } + /* here we should flush the last entry */ + + qsort(rle, n_entries, sizeof(RleEntry), cmp_rle); +} + + +#if 0 +static void dump_rle_entries(void) +{ + int i; + for (i = 0; i < n_entries; i++) { + size_t n = rle[i].n; + mem_ptr_t sa = rle[i].sa; + mem_ptr_t ea = sa + n; + uint8_t val = rle[i].val; + + if (n == 0) { + continue; + } + + printf("slot: $%04X-$%04X = $%02X (%zu bytes)\n", sa, ea - 1, val, n); + } +} +#endif + + +/************************************************************************** + * + * SECTION folding logic + * + ******/ +static int generate_copy(uint8_t *buf, mem_ptr_t sa, mem_ptr_t da, size_t n) +{ + uint8_t *p = buf; + + /* generate copy */ + MNE_LDXI(p, n); + int lp1 = p - buf; + MNE_LDAX(p, sa - 1); + MNE_STAX(p, da - 1); + MNE_DEX(p); + int br1 = p - buf; + MNE_BNE(p, lp1 - br1); + + return p - buf; +} + +static int generate_fill(uint8_t *buf, mem_ptr_t ta, size_t n, uint8_t val) +{ + uint8_t *p = buf; + + MNE_LDXI(p, n); + MNE_LDAI(p, val); + int lp1 = p - buf; + MNE_STAX(p, ta - 1); + MNE_DEX(p); + int br1 = p - buf; + MNE_BNE(p, lp1 - br1); + + return p - buf; +} + + +static int generate_trampoline(uint8_t *buf, mem_ptr_t ca, mem_ptr_t ta, size_t n, uint8_t val, mem_ptr_t sa, SfxConfig *conf) +{ + uint8_t *p = buf; + int tl; + mem_ptr_t tra = 0xd000; + + /* length of trampoline */ + tl = (conf->flags & FLAG_NOCLI) ? 9 : 10; + + /* if trampoline fits below the decrunched data, put it there */ + if ( (0x0002 + tl) <= sa ) { + tra = sa - tl; + } + + /* generate copy */ + MNE_LDAI(p, conf->mem); + MNE_STAZ(p, 0x01); + + MNE_LDXI(p, tl); + int lp1 = p - buf; + MNE_LDAX(p, ca + 22 - 1); + MNE_STAX(p, tra - 1); + MNE_DEX(p); + int br1 = p - buf; + MNE_BNE(p, lp1 - br1); + MNE_LDXI(p, n); + MNE_LDAI(p, val); + MNE_JMP(p, tra); + + int lp2 = p - buf; + MNE_STAX(p, ta - 1); + MNE_DEX(p); + int br2 = p - buf; + MNE_BNE(p, lp2 - br2); + if ( !(conf->flags & FLAG_NOCLI) ) { + MNE_CLI(p); + } + MNE_JMP(p, conf->jump); + + + return p - buf; +} + +static void do_fold_a(Memory *smem, mem_ptr_t low_limit, SfxConfig *conf) +{ + mem_ptr_t sa = smem->low; + + mem_ptr_t ad = low_limit; + mem_ptr_t last_ca = 0; + int last_val = -1; + int last_n = -1; + uint8_t *last_jmpaddr = 0; + int i; + + int count = 0; + int areas = 0; + + for (i = 0; i < n_entries; i++) { + mem_ptr_t ca = rle[i].sa; + int n = rle[i].n; + int val = rle[i].val; + uint8_t *buf = smem->buf + ca; + uint8_t *p = buf; + int left = ad - sa; + + if (last_jmpaddr) { + OP_ADR(last_jmpaddr, ca); + last_jmpaddr = 0; + } + + if (left <= 0) { + p += generate_fill(p, last_ca, last_n, last_val); + generate_trampoline(p, ca+10, ca, n, val, sa, conf); + + /* the counters should be possible to unify */ + areas++; + count += p - buf; + break; + } + + if (last_n < 0) { + int nmax = n - 14; + if ( left < nmax ) { + nmax = left; + n = nmax + 14; + } + ad -= nmax; + p += generate_copy(p, ca+14, ad, nmax); + last_jmpaddr = p + 1; + MNE_JMP(p, 0x0000); + memcpy(p, smem->buf + ad, nmax); + p += nmax; + } else { + int nmax = n - 24; + if ( left < nmax ) { + nmax = left; + n = nmax + 24; + } + ad -= nmax; + p += generate_fill(p, last_ca, last_n, last_val); + p += generate_copy(p, ca+24, ad, nmax); + last_jmpaddr = p + 1; + MNE_JMP(p, 0x0000); + memcpy(p, smem->buf + ad, nmax); + p += nmax; + } + + areas++; + count += p - buf; + + last_ca = ca; + last_n = n; + last_val = val; + } + + conf->mem = 0x34; + conf->flags |= FLAG_NOCLI; + conf->jump = rle[0].sa; + + smem->low = low_limit; + + printf("folded $%04x-$%04x into %d chunks (%d bytes overhead)\n", sa, low_limit, areas, count - (low_limit-sa)); +} + + +/************************************************************************** + * + * NAME fold() + * + * DESCRIPTION + * Fold low part in rle segments. + * + ******/ +SfxConfig *fold(Memory *smem, Memory *dmem, mem_ptr_t low_limit, mem_ptr_t high_limit, SfxConfig *conf) +{ + mem_ptr_t sa = smem->low; + mem_ptr_t ea = smem->high; + + if (sa >= low_limit) { + /* no fold required */ + return conf; + } +#if 0 + size_t n = low_limit - sa; + + if (ea <= high_limit - n) { + /* simple copy possible (should consider the size of the copy routine) */ + printf("simple fold possible\n"); + return conf; + } +#endif + /* perform full fold */ + + /* find rle slots above the desired low limit */ + scan_rle(smem, low_limit, ea); + + do_fold_a(smem, low_limit, conf); + + //dump_rle_entries(); + + return conf; +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/fold.h b/loader/tools/subsizer-0.7pre1/src/sfx/fold.h new file mode 100644 index 0000000..138aabd --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/fold.h @@ -0,0 +1,20 @@ +/************************************************************************** + * + * FILE fold.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#ifndef FOLD_H +#define FOLD_H + +#include "generate_sfx.h" +#include "../memory.h" + +SfxConfig *fold(Memory *smem, Memory *dmem, mem_ptr_t low_limit, mem_ptr_t high_limit, SfxConfig *conf); + + +#endif /* FOLD_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.c b/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.c new file mode 100644 index 0000000..fa18e50 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.c @@ -0,0 +1,441 @@ +/************************************************************************** + * + * FILE generate_sfx.c + * Copyright (c) 2015, 2016, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#include +#include +#include +#include + +#include "decrunchers_data.h" +#include "generate_sfx.h" +#include "ops6502.h" +#include "../buffer.h" +#include "../global.h" +#include "../memory.h" + + +static size_t wrap_len; +static mem_ptr_t tail_addr; +static mem_ptr_t src_end_addr; +static mem_ptr_t wrap_src_addr; +static mem_ptr_t decr_store_addr; +static mem_ptr_t decr_addr; +static size_t decr_len; +static uint8_t first_bitbuf; +static int endm_v; +static mem_ptr_t dest_end_addr; + + +uint8_t header_buf[256]; +int header_l; + +uint8_t prologue_buf[256]; +int prologue_l; + +uint8_t epilogue_buf[16]; +int epilogue_l; + + +/************************************************************************** + * + * SECTION common stage processing + * + ******/ +static SfxConfig config; + +#define FLAG_SYSLESS (FLAG_XBASE << 0) + +static void get_options(char *opts) +{ + char *next; + char *curr; + + if (*opts == 0) { + return; + } + + curr = opts; + do { + char *val; + next = strchr(curr, ','); + if (next) { + *next++ = 0; + } + + val = strchr(curr, '='); + if (val) { + *val++ = 0; + } + + if (debug_g) { + printf("flag: %s, val: %s\n", curr, val); + } + + if (strcmp("clean", curr) == 0) { + config.flags &= ~FLAG_DIRTY; + config.fold = 0x400; + continue; + } + if (strcmp("dirty", curr) == 0) { + config.flags |= FLAG_DIRTY; + config.fold = 0x200; + continue; + } + if (strcmp("fold", curr) == 0) { + if (val) { + uint16_t v; + v = strtoul(val, 0, 0); + config.fold = v; + } else { + config.fold = 0x200; + } + continue; + } + if (strcmp("nofold", curr) == 0) { + config.fold = 0; + continue; + } + if (strcmp("sei", curr) == 0) { + config.flags |= FLAG_NOCLI; + continue; + } + if (strcmp("mem", curr) == 0) { + uint8_t v; + v = strtoul(val, 0, 0); + config.mem = v; + continue; + } + if (strcmp("jmp", curr) == 0) { + uint16_t v; + v = strtoul(val, 0, 0); + config.jump = v; + continue; + } + if (strcmp("sysless", curr) == 0) { + config.flags |= FLAG_SYSLESS; + continue; + } + if (strcmp("load", curr) == 0) { + uint16_t v; + v = strtoul(val, 0, 0); + config.loadback = v; + config.flags |= FLAG_SYSLESS; + continue; + } + panic("unknown option: %s", curr); + } while ( (curr = next) ); + +} + + +SfxConfig *prepare_sfx(int num_opts, char **opts, int jmp) +{ + + config.loadback = 0x0801; + config.mem = 0x37; + config.mem_during = 0x34; + config.jump = jmp; + config.fold = 0x0400; + config.flags = 0; + + while (num_opts) { + get_options(*opts++); + num_opts--; + } + + return &config; +} + + +/************************************************************************** + * + * SECTION variants + * + ******/ +static FixStruct *find_variant(FixStruct *fs, int flags) +{ + while (fs->addr) { + if (fs->flags == (flags & FLAG_MASK)) { + return fs; + } + fs++; + } + + panic("couldn't find variant"); + return 0; +} + + +#if 0 +/************************************************************************** + * + * SECTION common stage processing + * + ******/ +static void scan_ft(FixStruct *fs) +{ + FixEntry *fe = fs->fix_entry; + + for (; fe->type != ftEnd; fe++) { + int offs = fe->addr - fs->addr; + + switch (fe->type) { + default: + break; + } + } +} +#endif + +static int process_ft(Memory *mem, mem_ptr_t sa, FixStruct *fs) +{ + int i; + FixEntry *fe = fs->fix_entry; + int len = fs->len; + + for (i = 0; i < len; i++) { + set_byte(mem, sa + i, fs->data[i] ^ XOR_MAGIC); + } + + for (; fe->type != ftEnd; fe++) { + mem_ptr_t ad = sa + fe->addr - fs->addr; + switch (fe->type) { + case ftBufZp: + set_byte(mem, ad, first_bitbuf); + break; + case ftDestEnd: + set_word(mem, ad, dest_end_addr); + break; + case ftEndMarkerMinusOne: + set_byte(mem, ad, endm_v - 1); + break; + case ftSrcEnd: + set_word(mem, ad, src_end_addr); + break; + default: + break; + } + } + + return len; +} + + + + +static int generate_header(uint8_t *buf) +{ + uint8_t *p = buf; + + /* generate memory setup */ + MNE_SEI(p); + MNE_LDAI(p, config.mem_during); + MNE_STAZ(p, 0x01); + MNE_JMP(p, tail_addr); + + return p - buf; +} + + +static int generate_prologue(uint8_t *buf, mem_ptr_t ca, mem_ptr_t la, int xy) +{ + uint8_t *p = buf; + +#if 0 + /* generate memory setup */ + MNE_SEI(p); + MNE_LDAI(p, 0x34); + MNE_STAZ(p, 0x01); +#endif + + mem_ptr_t src1, dest1; + size_t len1; + mem_ptr_t src2, dest2; + size_t len2; + + src1 = decr_store_addr; + dest1 = decr_addr; + len1 = decr_len; + + src2 = wrap_src_addr; + dest2 = la; + len2 = wrap_len; + + /* generate copy */ + MNE_LDXYI(p, len1, xy); + if ( xy & (dest2 <= 0x1f6) ) { + /* + * this is a bit kludgy: sets stack to ~$CA on the dirty decruncher + * if required for safe uncrunch. FIXME: should check if overlap! + */ + MNE_TXS(p); + } + int lp1 = p - buf; + MNE_LDAXY(p, src1 - 1, xy); + MNE_STAXY(p, dest1 - 1, xy); + if (len2 && len1 > len2) { + MNE_CPXYI(p, len2 + 1, xy); + MNE_BCS(p, 8); + MNE_LDAXY(p, src2 - 1, xy); + MNE_STAXY(p, dest2 - 1, xy); + } + MNE_DEXY(p, xy); + int br1 = p - buf; + MNE_BNE(p, lp1 - br1); + + if (len2 > len1) { + int offs = (0x100-len2) & 0xff; + int pages = (len2 >> 8) + 1; /* fix me */ + + MNE_LDXYI(p, pages, !xy); + MNE_LDXYI(p, offs, xy); + int lp2 = p - buf; + MNE_LDAXY(p, src2 - offs, xy); + MNE_STAXY(p, dest2 - offs, xy); + MNE_INXY(p, xy); + int br2 = p - buf; + MNE_BNE(p, lp2 - br2); + MNE_INC(p, ca + lp2 + 2); + MNE_INC(p, ca + lp2 + 5); + MNE_DEXY(p, !xy); + int br3 = p - buf; + MNE_BNE(p, lp2 - br3); + } + + + return p - buf; +} + + +static int generate_epilogue(uint8_t *buf) +{ + uint8_t *p = buf; + + /* generate memory setup */ + if ( config.mem != config.mem_during ) { + MNE_LDAI(p, config.mem); + MNE_STAZ(p, 0x01); + } + + /* generate CLI */ + if ( !(config.flags & FLAG_NOCLI) ) { + MNE_CLI(p); + } + + /* generate jump */ + MNE_JMP(p, config.jump); + + return p - buf; +} + + +int generate_sfx(Buffer *sbf, Memory *dmem, Memory *smem, int safe, int endm, SfxConfig *conf) +{ + /* should actually use conf instead of config directly */ + mem_ptr_t dest_end = smem->high; + + mem_ptr_t sa = config.loadback; + mem_ptr_t la = smem->low - safe; + + mem_ptr_t ca = sa; + mem_ptr_t ea = sa; + int xy = 0; + + if (config.flags & FLAG_DIRTY) { + xy = 1; + } + + + FixStruct *fs_header = find_variant(fs_list_header, config.flags); + FixStruct *fs_tail = find_variant(fs_list_tail, config.flags); + FixStruct *fs_decruncher = find_variant(fs_list_decruncher, config.flags); + + endm_v = endm; + dest_end_addr = dest_end; + + /* pop first bitbuf from the end of the stream */ + first_bitbuf = sbf->buf[sbf->len-1]; + sbf->len--; + + + /****** + * generate header (= sys + memory init + jump) + * + */ + mem_ptr_t header_end; + header_l = generate_header(header_buf); + header_end = sa + header_l; + if ( !(config.flags & FLAG_SYSLESS) ) { + header_end += fs_header->len; + } + + /* + * determine if the header overlaps the highest possible start + * of the packed data + */ + if (header_end > la) { + /* yes, we need an overlap */ + wrap_len = header_end - la; + } else { + /* no, force the start of the packed data to be where it already is */ + wrap_len = 0; + la = header_end; + } + src_end_addr = la + sbf->len; + wrap_src_addr = src_end_addr; + tail_addr = src_end_addr + wrap_len; + generate_header(header_buf); + + /* emit header */ + if ( !(config.flags & FLAG_SYSLESS) ) { + ca += process_ft(dmem, ca, fs_header); + } + ca += insert_mem(dmem, ca, header_buf, header_l); + + /* emit packed data (wrap_len may be 0) */ + ca += insert_mem(dmem, ca, sbf->buf + wrap_len, sbf->len - wrap_len); + ca += insert_mem(dmem, ca, sbf->buf, wrap_len); + + + /****** + * generate decruncher + * + */ + epilogue_l = generate_epilogue(epilogue_buf); + decr_addr = fs_decruncher->addr; + decr_len = fs_decruncher->len + epilogue_l; + + + /****** + * generate prologue + tail + * + */ + prologue_l = generate_prologue(prologue_buf, ca, la, xy); + decr_store_addr = ca + prologue_l + fs_tail->len; + generate_prologue(prologue_buf, ca, la, xy); + + /* emit prologue + tail */ + ca += insert_mem(dmem, ca, prologue_buf, prologue_l); + ca += process_ft(dmem, ca, fs_tail); + + /* emit decruncher */ + ca += process_ft(dmem, ca, fs_decruncher); + ca += insert_mem(dmem, ca, epilogue_buf, epilogue_l); + + + /* clean up */ + ea = ca; + + dmem->low = sa; + dmem->high = ea; + + return 0; +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.h b/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.h new file mode 100644 index 0000000..0632805 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/generate_sfx.h @@ -0,0 +1,32 @@ +/************************************************************************** + * + * FILE generate_sfx.h + * Copyright (c) 2015, 2016 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#ifndef GENERATE_SFX_H +#define GENERATE_SFX_H + +#include +#include "../buffer.h" +#include "../memory.h" + + +typedef struct { + mem_ptr_t loadback; + uint8_t mem; + uint8_t mem_during; + mem_ptr_t jump; + mem_ptr_t fold; + int flags; +} SfxConfig; + + +SfxConfig *prepare_sfx(int num_opts, char **opts, int jmp); +int generate_sfx(Buffer *sbf, Memory *dmem, Memory *smem, int safe, int endm, SfxConfig *conf); + +#endif /* GENERATE_SFX_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.c b/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.c new file mode 100644 index 0000000..e3d88cf --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.c @@ -0,0 +1,48 @@ +/************************************************************************** + * + * FILE mach_c64.c + * Copyright (c) 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ + +#include "../memory.h" + + +/************************************************************************** + * + * NAME is_basic(), is_io(), is_kernal() + * + * DESCRIPTION + * Identify if address is within special section. + * + ******/ +int is_basic(mem_ptr_t ad) +{ + if (ad >= 0xa000 && ad <= 0xbfff) { + return 1; + } + return 0; +} + +int is_io(mem_ptr_t ad) +{ + if (ad >= 0xd000 && ad <= 0xdfff) { + return 1; + } + return 0; +} + +int is_kernal(mem_ptr_t ad) +{ + if (ad >= 0xe000 && ad <= 0xffff) { + return 1; + } + return 0; +} + + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.h b/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.h new file mode 100644 index 0000000..cc0172c --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/mach_c64.h @@ -0,0 +1,20 @@ +/************************************************************************** + * + * FILE mach_c64.h + * Copyright (c) 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#ifndef MACH_C64_H +#define MACH_C64_H + +#include "../memory.h" + +int is_basic(mem_ptr_t ad); +int is_io(mem_ptr_t ad); +int is_kernal(mem_ptr_t ad); + +#endif /* MACH_C64_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/sfx/ops6502.h b/loader/tools/subsizer-0.7pre1/src/sfx/ops6502.h new file mode 100644 index 0000000..0766710 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/sfx/ops6502.h @@ -0,0 +1,63 @@ +/************************************************************************** + * + * FILE ops6502.h + * Copyright (c) 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * + ******/ +#ifndef OPS6502_H +#define OPS6502_H + + +#define OP_ADR(p, ad) { *p++ = (ad) & 0xff; *p++ = ((ad) >> 8) & 0xff; } + +#define MNE_CLD(p) { *p++ = 0xd8; } +#define MNE_CLI(p) { *p++ = 0x58; } +#define MNE_SED(p) { *p++ = 0xf8; } +#define MNE_SEI(p) { *p++ = 0x78; } +#define MNE_LDAI(p, imm) { *p++ = 0xa9; *p++ = imm; } +#define MNE_STAZ(p, zp) { *p++ = 0x85; *p++ = zp; } +#define MNE_JMP(p, ad) { *p++ = 0x4c; OP_ADR(p, ad); } + +#define MNE_TAX(p) { *p++ = 0xaa; } +#define MNE_TSX(p) { *p++ = 0xba; } +#define MNE_TAY(p) { *p++ = 0xa8; } +#define MNE_TXA(p) { *p++ = 0x8a; } +#define MNE_TXS(p) { *p++ = 0x9a; } +#define MNE_TYA(p) { *p++ = 0x98; } + + +#define MNE_BCS(p, offs) { *p++ = 0xb0; *p++ = offs - 2; } +#define MNE_BNE(p, offs) { *p++ = 0xd0; *p++ = offs - 2; } + +#define MNE_LDAI(p, imm) { *p++ = 0xa9; *p++ = imm; } +#define MNE_LDXI(p, imm) { *p++ = 0xa2; *p++ = imm; } +#define MNE_LDYI(p, imm) { *p++ = 0xa0; *p++ = imm; } +#define MNE_LDAX(p, ad) { *p++ = 0xbd; OP_ADR(p, ad); } +#define MNE_LDAY(p, ad) { *p++ = 0xb9; OP_ADR(p, ad); } +#define MNE_STAX(p, ad) { *p++ = 0x9d; OP_ADR(p, ad); } +#define MNE_STAZX(p, zp) { *p++ = 0x95; *p++ = zp; } +#define MNE_STAY(p, ad) { *p++ = 0x99; OP_ADR(p, ad); } +#define MNE_DEX(p) { *p++ = 0xca; } +#define MNE_DEY(p) { *p++ = 0x88; } +#define MNE_INX(p) { *p++ = 0xe8; } +#define MNE_INY(p) { *p++ = 0xc8; } +#define MNE_CPXI(p, imm) { *p++ = 0xe0; *p++ = imm; } +#define MNE_CPYI(p, imm) { *p++ = 0xc0; *p++ = imm; } + +#define MNE_INC(p, ad) { *p++ = 0xee; OP_ADR(p, ad); } +#define MNE_DEC(p, ad) { *p++ = 0xce; OP_ADR(p, ad); } + +/* special X/Y optional */ +#define MNE_LDXYI(p, imm, xy) if (xy) MNE_LDXI(p, imm) else MNE_LDYI(p, imm) +#define MNE_LDAXY(p, ad, xy) if (xy) MNE_LDAX(p, ad) else MNE_LDAY(p, ad) +#define MNE_STAXY(p, ad, xy) if (xy) MNE_STAX(p, ad) else MNE_STAY(p, ad) +#define MNE_DEXY(p, xy) if (xy) MNE_DEX(p) else MNE_DEY(p) +#define MNE_INXY(p, xy) if (xy) MNE_INX(p) else MNE_INY(p) +#define MNE_CPXYI(p, imm, xy) if (xy) MNE_CPXI(p, imm) else MNE_CPYI(p, imm) + + +#endif /* OPS6502_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/subsizer.c b/loader/tools/subsizer-0.7pre1/src/subsizer.c new file mode 100644 index 0000000..0ea804c --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/subsizer.c @@ -0,0 +1,460 @@ +/************************************************************************** + * + * FILE subsizer.c + * Copyright (c) 2012, 2015, 2016, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * CBM packer/cruncher + * + ******/ +#include +#include +#include +#include +#include +#include + +#include "bitfunc.h" +#include "buffer.h" +#include "crunch_normal.h" +#include "pathfinder.h" +#include "global.h" +#include "match.h" +#include "memory.h" +#include "message.h" +#include "params.h" +#include "sfx/detect_start.h" +#include "utils.h" + +#define MAX_DECRUNCH_SIZE 0x100000 + +#define HAVE_CRUNCH_LEVEL 0 + + +#define PROGRAM "subsizer" + +const char program_g[] = PROGRAM; + + +typedef struct { + char *name; + int (*pack_func)(Buffer *, Buffer *); + int (*depack_func)(Buffer *, Buffer *); + int (*mem_func)(Memory *, Memory *, int num_opts, char **opts); + int (*sfx_func)(Memory *, Memory *, int num_opts, char **opts, int jmp); +} Type; + +Type types[] = { + { /* crunch_normal */ + "normal", + crunch_normal, decrunch_normal, + crunch_normal_mem, + crunch_normal_sfx + }, + { 0, 0, 0, 0, 0 } +}; + + +static int crunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode); +static int decrunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode); + +static int crunch_mem(Type *p, int num_files, char **srcnames, char *destname, int forwards_mode); +#if HAVE_CRUNCH_LEVEL +static int crunch_level(Type *p, int num_files, char **srcnames, char *destname); +#endif + +static int crunch_sfx(Type *p, int num_files, char **srcnames, char *destname, int num_sfx_opts, char **sfx_opts); + + + +int main(int argc, char *argv[]) +{ + int c; + char **srcnames = 0; + char *destname = "a.out"; + enum { MODE_RAW, MODE_SFX, MODE_MEM, MODE_LEVEL } mode; + int num_sfx_opts = 0; + char *sfx_opts[256]; + int backwards_mode; + int forwards_mode; + int depack_mode; + char *pack_type; + + + /* defaults */ + verbose_g = 1; + debug_g = 0; + mode = MODE_RAW; /* always raw mode for now */ + backwards_mode = 0; + forwards_mode = 0; + depack_mode = 0; + pack_type = "normal"; + + /* + * scan for valid options + */ + while (EOF != (c = getopt(argc, argv, "o:t:rmlxX:bfdqvDVh"))) { + switch (c) { + + /* a missing parameter */ + case ':': + /* an illegal option */ + case '?': + exit(1); + + /* set quiet mode */ + case 'q': + verbose_g = 0; + break; + + /* set verbose mode */ + case 'v': + verbose_g++; + break; + + /* set debug mode */ + case 'D': + debug_g = 1; + break; + + /* print version */ + case 'V': + fprintf (stdout, PROGRAM " " VERSION "\n"); + exit(0); + + /* print help */ + case 'h': + fprintf(stderr, +PROGRAM " " VERSION ": CBM packer/cruncher\n" +"Copyright (c) 2012, 2015, 2016, 2017 Daniel Kahlin \n" +"Written by Daniel Kahlin \n" +"\n" +"usage: " PROGRAM " [OPTION] FILE...\n" +"\n" +"Valid options:\n" +" -t select algorithm type (default: normal)\n" +" -r raw file\n" +" -m memory file\n" +#if HAVE_CRUNCH_LEVEL +" -l level file\n" +#endif +" -x executable file\n" +" -X executable options (implies '-x')\n" +" -b backwards mode\n" +" -f forwards mode\n" +" -d decompress\n" +" -o output file\n" +" -q be quiet\n" +" -v be verbose (can be multiple)\n" +" -D display debug information\n" +" -h displays this help text\n" +" -V output program version\n" + ); + exit(0); + + /* set destination name */ + case 'o': + destname = optarg; + break; + + /* set algorithm type */ + case 't': + pack_type = optarg; + break; + + /* run in raw mode */ + case 'r': + mode = MODE_RAW; + break; + + /* run in memory mode */ + case 'm': + mode = MODE_MEM; + break; + + /* run in level mode */ + case 'l': + mode = MODE_LEVEL; + break; + + /* set executable mode */ + case 'x': + mode = MODE_SFX; + break; + + /* set executable options (implying -x) */ + case 'X': + mode = MODE_SFX; + sfx_opts[num_sfx_opts] = optarg; + num_sfx_opts++; + break; + + /* run in backwards mode */ + case 'b': + backwards_mode = 1; + break; + + /* run in forwards mode */ + case 'f': + forwards_mode = 1; + break; + + /* run in depack mode */ + case 'd': + depack_mode = 1; + break; + + /* default behavior */ + default: + break; + } + } + + /* + * optind now points at the first non option argument + * expect one or more arguments + */ + int num_files = argc - optind; + if (num_files < 1) { + panic("too few arguments"); + } + srcnames = &argv[optind]; + + Type *p = types; + while (p->name) { + if (strcmp(p->name, pack_type) == 0) { + break; + } + p++; + } + if (!p->name) { + panic("unknown type '%s'", pack_type); + } + + + if (!depack_mode) { + /* handle crunching */ + switch (mode) { + case MODE_RAW: + crunch_raw(p, num_files, srcnames, destname, backwards_mode); + break; + case MODE_MEM: + crunch_mem(p, num_files, srcnames, destname, forwards_mode); + break; +#if HAVE_CRUNCH_LEVEL + case MODE_LEVEL: + crunch_level(p, num_files, srcnames, destname); + break; +#endif + case MODE_SFX: + + if (!p->sfx_func) { + panic("sfx not available for '%s'", p->name); + } + crunch_sfx(p, num_files, srcnames, destname, num_sfx_opts, sfx_opts); + break; + default: + panic("internal: invalid mode (%d)", mode); + break; + } + } else { + /* handle decrunching */ + switch (mode) { + case MODE_RAW: + decrunch_raw(p, num_files, srcnames, destname, backwards_mode); + break; + case MODE_SFX: + panic("cannot depack sfx"); + break; + default: + panic("internal: invalid mode (%d)", mode); + break; + } + } + + + exit(0); +} + + +static int crunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode) +{ + int ret; + Buffer *sbf; + Buffer *dbf; + + /* this should handle more than one file */ + sbf = create_buffer_from_file(srcnames[0]); + + dbf = create_buffer(MAX_DECRUNCH_SIZE); + + if (backwards_mode) { + reverse_buffer(sbf); + } + + ret = p->pack_func(sbf, dbf); + if (ret) { + printf("warning: packing failed. packer returned non-zero!\n"); + } + + msg(MSG_VERBOSE, "packed %zu bytes into %zu bytes\n", sbf->len, dbf->len); + +#if 1 + /* verify */ + Buffer *vbf; + vbf = create_buffer(MAX_DECRUNCH_SIZE); + ret = p->depack_func(dbf, vbf); + if (ret) { + printf("warning: verify failed. depacker returned non-zero!\n"); + } else if (compare_buffer(sbf, vbf) != 0) { + printf("warning: verify failed. depacked data does not match source data!\n"); + } else { + msg(MSG_VERBOSE, "verifed %zu bytes...ok\n", sbf->len); + } + destroy_buffer(vbf); +#endif + + if (backwards_mode) { + reverse_buffer(dbf); + } + + write_buffer_to_file(dbf, destname); + + destroy_buffer(dbf); + destroy_buffer(sbf); + + return 0; +} + + + +static int decrunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode) +{ + int ret; + Buffer *sbf; + Buffer *dbf; + + /* this should handle more than one file */ + sbf = create_buffer_from_file(srcnames[0]); + + dbf = create_buffer(MAX_DECRUNCH_SIZE); + + if (backwards_mode) { + reverse_buffer(sbf); + } + + ret = p->depack_func(sbf, dbf); + if (ret) { + printf("warning: depacking failed. depacker returned non-zero!\n"); + } + msg(MSG_VERBOSE, "depacked %zu bytes into %zu bytes\n", sbf->len, dbf->len); + + if (backwards_mode) { + reverse_buffer(dbf); + } + + write_buffer_to_file(dbf, destname); + + destroy_buffer(dbf); + destroy_buffer(sbf); + + return 0; +} + + +static int crunch_mem(Type *p, int num_files, char **srcnames, char *destname, int forwards_mode) +{ + int i; + int ret; + Memory *smem; + Memory *dmem; + int srclen, destlen; + + smem = create_memory(0x10000); + dmem = create_memory(0x10000); + + for (i = 0; i < num_files; i++) { + char *name = srcnames[i]; + file_t f; + + f = parse_filename(name); + load_mem(smem, &f, 0, 0); + } + srclen = smem->high - smem->low; + + // ret = p->mem_func(smem, dmem, 0, 0); + ret = p->mem_func(smem, dmem, forwards_mode, 0); // FIXME: kludge!!!! + if (ret) { + printf("warning: packing failed. packer returned non-zero!\n"); + } + + destlen = dmem->high - dmem->low; + msg(MSG_VERBOSE, "packed %d bytes (%d blocks) into %d bytes (%d blocks)\n", srclen, (srclen+2+253)/254, destlen, (destlen+2+253)/254 ); + + save_file_from_memory(dmem, destname); + + destroy_memory(dmem); + destroy_memory(smem); + + return 0; +} + + +static int crunch_sfx(Type *p, int num_files, char **srcnames, char *destname, int num_sfx_opts, char **sfx_opts) +{ + int i; + int ret; + Memory *smem; + Memory *dmem; + int srclen, destlen; + int jmp = -1; + + smem = create_memory(0x10000); + dmem = create_memory(0x10000); + + for (i = 0; i < num_files; i++) { + char *name = srcnames[i]; + file_t f; + + f = parse_filename(name); + load_mem(smem, &f, 0, 0); + } + srclen = smem->high - smem->low; + + if (jmp < 0) { + int r; + r = detect_start(smem); +#if 0 + if (r < 0) { + panic("couldn't detect start address!"); + } +#endif + jmp = r; + + msg(MSG_VERBOSE, "detected sys: $%04X\n", jmp); + } + + /* if presumable basic start, make sure $0800 ends up $00 */ + if (smem->low == 0x0801) { + set_byte(smem, 0x0800, 0x00); + smem->low = 0x0800; + } + + ret = p->sfx_func(smem, dmem, num_sfx_opts, sfx_opts, jmp); + if (ret) { + printf("warning: packing failed. packer returned non-zero!\n"); + } + + destlen = dmem->high - dmem->low; + msg(MSG_VERBOSE, "packed %d bytes (%d blocks) into %d bytes (%d blocks)\n", srclen, (srclen+2+253)/254, destlen, (destlen+2+253)/254 ); + + save_file_from_memory(dmem, destname); + + destroy_memory(dmem); + destroy_memory(smem); + + return 0; +} + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/universal.c b/loader/tools/subsizer-0.7pre1/src/universal.c new file mode 100644 index 0000000..1976ba1 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/universal.c @@ -0,0 +1,108 @@ +/************************************************************************** + * + * FILE universal.c + * Copyright (c) 2015 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * universal encodings + * + ******/ +#include + +#include "bitfunc.h" +#include "universal.h" +#include "global.h" + + +/************************************************************************** + * + * NAME cost_unary, write_unary, read_unary + * + ******/ +int cost_unary(int v, int lim) +{ + if (v == lim-1) { + return v; + } + return v+1; +} + + +void write_unary(BitWriteState *bws, int v, int lim, int pol) +{ + uint32_t mask = pol ? 0xfffffff : 0; + int endbit = pol ? 0 : 1; + + bitwr_write(bws, mask, v); + if (v < lim-1) { + bitwr_write(bws, endbit, 1); + } +} + + +int read_unary(BitReadState *brs, int lim, int pol) +{ + int n = 0; + while (bitrd_read(brs, 1) == pol) { + n++; + if (n == lim-1) { + break; + } + } + return n; +} + + +/************************************************************************** + * + * NAME cost_eliasgamma, write_eliasgamma, read_eliasgamma + * + ******/ +int cost_eliasgamma(int v) +{ + int n; + + /* find first 1 */ + for (n = 31; n >= 0; n--) { + if ( (v>>n) & 0x01) + break; + } + return n + n+1; +} + + +void write_eliasgamma(BitWriteState *bws, int v) +{ + int n; + /* find first 1 */ + for (n = 31; n >= 0; n--) { + if ( (v>>n) & 0x01) + break; + } + + bitwr_write(bws, 0, n); + bitwr_write(bws, v, n+1); +} + + +int read_eliasgamma(BitReadState *brs) +{ + int v; + int n = 0; + while (bitrd_read(brs, 1) == 0) { + n++; + } + if (n == 16) { + /* short cut to end marker */ + return 0x10000; + } + + v = bitrd_read(brs, n); + v |= 1< + * Written by Daniel Kahlin + * + * DESCRIPTION + * universal encodings + * + ******/ +#ifndef UNIVERSAL_H +#define UNIVERSAL_H + +#include "bitfunc.h" + +int cost_unary(int v, int lim); +void write_unary(BitWriteState *bws, int v, int lim, int pol); +int read_unary(BitReadState *brs, int lim, int pol); + +int cost_eliasgamma(int v); +void write_eliasgamma(BitWriteState *bws, int v); +int read_eliasgamma(BitReadState *brs); + +#endif /* UNIVERSAL_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/utils.c b/loader/tools/subsizer-0.7pre1/src/utils.c new file mode 100644 index 0000000..5c2d2f7 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/utils.c @@ -0,0 +1,93 @@ +/************************************************************************** + * + * FILE utils.c + * Copyright (c) 2012, 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * utility functions. + * + ******/ +#include +#include +#include + +#include "global.h" +#include "utils.h" + +/************************************************************************** + * + * SECTION memory functions + * + ******/ +void *safe_malloc(size_t size, const char *msg) +{ + void *ptr = malloc(size); + if (!ptr) { + panic("couldn't malloc: %s", msg); + } + + return ptr; +} + +void *safe_calloc(size_t nmemb, size_t size, const char *msg) +{ + void *ptr = calloc(nmemb, size); + if (!ptr) { + panic("couldn't calloc: %s", msg); + } + + return ptr; +} + +void *safe_realloc(void *ptr, size_t size, const char *msg) +{ + ptr = realloc(ptr, size); + if (!ptr) { + panic("couldn't realloc: %s", msg); + } + + return ptr; +} + + +char *safe_strdup(const char *str, const char *msg) +{ + char *dup = strdup(str); + if (!dup) { + panic("couldn't strdup: %s", msg); + } + + return dup; +} + + +#if 0 +/* apparently not present in mingw32 */ +char *safe_strndup(const char *str, int n, const char *msg) +{ + char *dup = strndup(str, n); + if (!dup) { + panic("couldn't strndup: %s", msg); + } + + return dup; +} +#endif + + +/************************************************************************** + * + * SECTION time functions + * + ******/ +double get_time(void) +{ + struct timeval tv; + gettimeofday(&tv, 0); + + return tv.tv_sec + 0.000001 * tv.tv_usec; +} + + +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/src/utils.h b/loader/tools/subsizer-0.7pre1/src/utils.h new file mode 100644 index 0000000..5aba7a3 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/src/utils.h @@ -0,0 +1,31 @@ +/************************************************************************** + * + * FILE utils.h + * Copyright (c) 2012, 2015, 2017 Daniel Kahlin + * Written by Daniel Kahlin + * + * DESCRIPTION + * utility functions. + * + ******/ +#ifndef UTILS_H +#define UTILS_H + +#include + +/* memory functions */ +void *safe_malloc(size_t size, const char *msg); +void *safe_calloc(size_t nmemb, size_t size, const char *msg); +void *safe_realloc(void *ptr, size_t size, const char *msg); +char *safe_strdup(const char *str, const char *msg); +#if 0 +/* apparently not present in mingw32 */ +char *safe_strndup(const char *str, int n, const char *msg); +#endif + +/* time functions */ +double get_time(void); + + +#endif /* UTILS_H */ +/* eof */ diff --git a/loader/tools/subsizer-0.7pre1/standalone/decrunch_normal.asm b/loader/tools/subsizer-0.7pre1/standalone/decrunch_normal.asm new file mode 100644 index 0000000..e022e43 --- /dev/null +++ b/loader/tools/subsizer-0.7pre1/standalone/decrunch_normal.asm @@ -0,0 +1,420 @@ +;************************************************************************** +;* +;* FILE decrunch_normal.asm +;* Copyright (c) 2015, 2017 Daniel Kahlin +;* Written by Daniel Kahlin +;* +;* DESCRIPTION +;* subsizer 0.7pre1 decruncher - stand alone version +;* +;* usage: +;* You need to provide a function to get a byte from the input +;* stream. (must preserve X,Y and C) +;* +;* typical function: +;* +;* dc_get_byte: +;* lda dcgb_ptr +;* bne dcgb_skp1 +;* dec dcgb_ptr+1 +;* dcgb_skp1: +;* dec dcgb_ptr +;* dcgb_ptr equ . + 1 +;* lda data_end +;* rts +;* +;* To decrunch just do: +;* +;* jsr decrunch +;* +;* The decruncher will use a temporary area of 188 bytes during +;* decrunching. +;* +;****** + + +;************************************************************************** +;* +;* Configuration options +;* +;****** +HAVE_LONG_PARTS equ 1 + + + if HAVE_LONG_PARTS +PART_MASK equ %00001111 +N_PARTS equ 16 + else +PART_MASK equ %00000111 +N_PARTS equ 8 + endif + + + seg.u zp + org $f8 +len_zp: + ds.b 1 +copy_zp: + ds.w 1 +hibits_zp: + ds.b 1 +buf_zp: + ds.b 1 +dest_zp: + ds.w 1 +endm_zp: + ds.b 1 + + + seg code + +;************************************************************************** +;* +;* NAME fast macros +;* +;****** + +;****** +;* get bit macro + mac get_bit + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + jsr dc_get_byte + rol + sta buf_zp +.gb_skp1: + endm + + +;****** +;* get bits max8 macro + mac get_bits_max8 +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta buf_zp + pla +.gb_skp1: + rol + dey + bne .gb_lp1 + endm + + +;****** +;* get bits max8 masked macro + mac get_bits_max8_masked +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta buf_zp + tya +.gb_skp1: + rol + bcs .gb_lp1 + endm + + +;****** +;* get bits max16 macro + mac get_bits_max16 +.gb_lp1: + asl buf_zp + bne .gb_skp1 +; C=1 (because the marker bit was just shifted out) + pha + jsr dc_get_byte + rol + sta buf_zp + pla +.gb_skp1: + rol + rol hibits_zp + dey + bne .gb_lp1 ; C=0 for all Y!=0 + endm + + +;************************************************************************** +;* +;* NAME decrunch +;* +;****** +decrunch: + ldx #4 +; Get dest_zp, endm_zp and buf_zp +dc_lp00: + jsr dc_get_byte + sta buf_zp-1,x + dex + bne dc_lp00 +; X = 0 + +; ldx #0 +dc_lp01: + +;****** +;* get 4 bits + lda #%11100000 +dcg_lp1: + asl buf_zp + bne dcg_skp1 +; C=1 (because the marker bit was just shifted out) + tay + jsr dc_get_byte + rol + sta buf_zp + tya +dcg_skp1: + rol + bcs dcg_lp1 +; Acc = 4 bits. + + sta bits,x + + txa + and #PART_MASK + tay + beq dc_skp01 + + lda #0 + sta hibits_zp + ldy bits-1,x + sec +dc_lp02: + rol + rol hibits_zp + dey + bpl dc_lp02 +; C = 0 +; clc + adc base_l-1,x + tay + lda hibits_zp + adc base_h-1,x + +dc_skp01: + sta base_h,x + tya + sta base_l,x + inx + cpx #N_PARTS*4+4 + bne dc_lp01 + +; perform decrunch + ldy #0 + ; fall through + +;************************************************************************** +;* +;* NAME decruncher +;* +;* DESCRIPTION +;* decruncher +;* +;****** +decrunch_entry: + +;****** +;* single literal byte +;* +dc_literal: + lda dest_zp + bne dc_skp5 + dec dest_zp+1 +dc_skp5: + dec dest_zp + jsr dc_get_byte +; ldy #0 +dc_common: + sta (dest_zp),y + ; fall through + +decrunch_main: +;------ +; perform actual decrunch +dc_lp1: + get_bit + bcs dc_literal + +; get length as bits/base. + ldx #$80-N_PARTS +dc_lp2: + inx + bmi dc_skp0 + get_bit + bcc dc_lp2 + clc +dc_skp0: +; C = 0, Y = 0 +; lda #0 + tya + ldy [bits_len-$80+N_PARTS-1],x + beq dcb1_skp2 + get_bits_max8 +dcb1_skp2: +; C = 0 + adc [base_len-$80+N_PARTS-1],x + sta len_zp +; C = 0 + +;****** +;* IN: len = $01..$100 (Acc = $00..$ff) +;* OUT: dest_zp = dest_zp - len, X = len-1 +;* + tax +; clc + eor #$ff + adc dest_zp + sta dest_zp + bcs dc_skp22 + dec dest_zp+1 +dc_skp22: + +; check end marker here to avoid thrashing carry earlier + cpx endm_zp + beq done + +;****** +;* Get selector bits depending on length. +;* +;* IN: len = $01..$100 (X = $00..$ff) +;* OUT: +;* + cpx #4 + bcc dc_skp2 + ldx #3 +dc_skp2: + +; get offset as bits/base. + lda tabb,x + get_bits_max8_masked + tax +; C = 0 + + lda #0 + sta hibits_zp + ldy bits_offs,x + beq dcb3_skp2 + get_bits_max16 +dcb3_skp2: +; C = 0, Acc/hibits_zp + base_offs,x = offset - 1 + +; perform: copy_zp = Acc/hibits_zp + base_offs,x + 1 + dest_zp +; result: copy_zp = dest_zp + offset + adc base_offs_l,x + bcc dcb3_skp3 + inc hibits_zp +dcb3_skp3: + sec + adc dest_zp + sta copy_zp + lda hibits_zp + adc base_offs_h,x +; C = 0 + adc dest_zp+1 + sta copy_zp+1 + +;****** +;* Reverse fast copy +;* +;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0 +;* +copy: + ldy len_zp + beq dc_skp4 +dc_lp4: + lda (copy_zp),y + sta (dest_zp),y + dey + bne dc_lp4 +dc_skp4: + lda (copy_zp),y +; sta (dest_zp),y + jmp dc_common +; bcc dc_common ; always taken + +;****** +;* exit out +done: + rts + + if HAVE_LONG_PARTS +tabb: + dc.b %10000000 | [48 >> 2] ; 2 bits + dc.b %11100000 | [0 >> 4] ; 4 bits + dc.b %11100000 | [16 >> 4] ; 4 bits + dc.b %11100000 | [32 >> 4] ; 4 bits + else +tabb: + dc.b %10000000 | [24 >> 2] ; 2 bits + dc.b %11000000 | [0 >> 3] ; 3 bits + dc.b %11000000 | [8 >> 3] ; 3 bits + dc.b %11000000 | [16 >> 3] ; 3 bits + endif + + + + if 0 +;************************************************************************** +;* +;* NAME dc_get_byte +;* +;* DESCRIPTION +;* Get byte from the packed stream. +;* +;****** +dc_get_byte: + lda dc_ptr + bne dcgb_skp1 + dec dc_ptr+1 +dcgb_skp1: + dec dc_ptr +dc_ptr equ . + 1 + lda.w $0000 + rts + endif + + +end_decruncher: + + seg.u tables + org $0334 +begin_tables: +;************************************************************************** +;* +;* NAME base_l, base_h, bits +;* +;* DESCRIPTION +;* Data for bits/base decoding. +;* +;****** +base_l: +base_len: + ds.b N_PARTS,0 +base_offs_l: + ds.b N_PARTS*3+4,0 +base_h equ . - N_PARTS +; ds.b N_PARTS,0 +base_offs_h: + ds.b N_PARTS*3+4,0 + +bits: +bits_len: + ds.b N_PARTS,0 +bits_offs: + ds.b N_PARTS*3+4,0 + +end_tables: + + seg code +; eof diff --git a/loader/tools/tinycrunch_v1.2/Makefile b/loader/tools/tinycrunch_v1.2/Makefile new file mode 100644 index 0000000..3e4b197 --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/Makefile @@ -0,0 +1,53 @@ +TEST = test/test.prg +FTEST = test/ftest.prg +TESTBIN = test/testbin.prg +FTESTBIN = test/ftestbin.prg +CLFLAGS = -C c64-asm.cfg -u __EXEHDR__ --cpu 6502X +RUNNER = x64sc + +all: $(TEST) $(FTEST) $(TESTBIN) $(FTESTBIN) tc_boot.prg + +release: tc_boot.prg bmp.prg bmp.bin + +run: $(TEST) + $(RUNNER) $< + +frun: $(FTEST) + $(RUNNER) $< + +runbin: $(TESTBIN) + $(RUNNER) $< + +frunbin: $(FTESTBIN) + $(RUNNER) $< + +tc_boot.prg: tc_boot.s tc_decode_f.s + cl65 $(CLFLAGS) -o $@ tc_boot.s + +$(TEST): test/test.s tc_decode.s test/params.inc crunched.prg test/cbmcat + cl65 $(CLFLAGS) -o test/decode.prg test/test.s tc_decode.s -Ln test/test.sym + #cl65 $(CLFLAGS) --asm-define TC_BLOCK_INTERFACE -o test/decode.prg test/test.s tc_decode.s -Ln test/test.sym + python test/cbmcat test/decode.prg crunched.prg >$@ + +$(FTEST): test/test.s tc_decode_f.s test/params.inc crunched.prg test/cbmcat + cl65 $(CLFLAGS) -o test/fdecode.prg test/test.s tc_decode_f.s -Ln test/ftest.sym + #cl65 $(CLFLAGS) --asm-define TC_BLOCK_INTERFACE -o test/fdecode.prg test/test.s tc_decode_f.s -Ln test/ftest.sym + python test/cbmcat test/fdecode.prg crunched.prg >$@ + +$(TESTBIN): test/testbin.s tc_decode.s crunched.bin + cl65 $(CLFLAGS) --asm-define TC_NO_HEADER -o $@ test/testbin.s tc_decode.s -Ln test/testbin.sym + +$(FTESTBIN): test/test.s tc_decode_f.s crunched.bin + cl65 $(CLFLAGS) --asm-define TC_NO_HEADER -o $@ test/testbin.s tc_decode_f.s -Ln test/ftestbin.sym + +bmp.prg bmp.bin: test/mkbmp.py + python $< + +test/params.inc crunched.prg: tc_encode.py bmp.prg Makefile + python $< -vi bmp.prg crunched.prg -p test/params.inc + +crunched.bin: tc_encode.py bmp.bin Makefile + python $< -vr bmp.bin crunched.bin + +clean: + $(RM) -rf *.{prg,o,sym} test/*.{prg,o,sym,inc} *.bin diff --git a/loader/tools/tinycrunch_v1.2/bmp.bin b/loader/tools/tinycrunch_v1.2/bmp.bin new file mode 100644 index 0000000..5397173 Binary files /dev/null and b/loader/tools/tinycrunch_v1.2/bmp.bin differ diff --git a/loader/tools/tinycrunch_v1.2/bmp.prg b/loader/tools/tinycrunch_v1.2/bmp.prg new file mode 100644 index 0000000..cb1bec8 Binary files /dev/null and b/loader/tools/tinycrunch_v1.2/bmp.prg differ diff --git a/loader/tools/tinycrunch_v1.2/readme.txt b/loader/tools/tinycrunch_v1.2/readme.txt new file mode 100644 index 0000000..8ed863b --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/readme.txt @@ -0,0 +1,122 @@ +/* + * TinyCrunch 1.2 + * Christopher Jam + * September 2018 + */ + +About +===== + +TinyCrunch is a small, fast LZ codec originally thrown together in a hurry for +Jam Ball 2. It's since been considerably refined. + +While the basic design of three byte aligned fixed length token formats remains +the same, there is now a 100 byte decoder for when space is even tighter, a +faster decoder (tc_decode_f.s) for use in self extracting files, and optional +'next block' callbacks for fastload integration. Last but not least, it can +also now output self extracting .prgs, for any file north of $0200. + + +Requirements +============ + +Generating self extracting .prgs only requires python (tested on 2.7 and 3.6) + +Building the tests, or modifying the boot file used by sfx mode requires ca65. + +Rebuilding bmp.prg/bmp.bin (the bitmap displayed by the tests) would require Numpy, +but the .prg and .bin are included in case you have python and ca65 but don't want +to install any more python libraries to run the example code. + + +Installation Notes +================== + +Wherever you put tc_encode.py, make sure it's accompanied by tc_boot.prg + + +Usage +===== + +python tc_encode.py -h for argument descriptions. Note that you must specify +precisely one of output start address, output end address, in-place compression, +self extracting, or raw (headerless) mode. + +The data files produced by the first three options can be decoded with either +of the tc_decode*.s routines by calling decode with A and X set to the low and +high bytes respectively of the address of the data to decrunch. + +cf SFX notes below for the self extracting option. + +The fifth option (-r) just reads & writes blocks of binary data with no load address. +The output file contains no header at all, so it's the caller's responsibility to +set the source and destination zero page pointers to source address and one less +than destination address respectively. Cf test/testbin.s for example usage, and +don't forget to define TC_NO_HEADER when assembling. + +In this mode the small decruncher is a mere 79 bytes long. Note that in-place +decompression is not supported in this instance, as the datastream is null terminated +and there's no header to save the final byte. + + +SFX notes +========= + +Simple sfx example: + + python tc_encode.py -vx input.prg output.prg + +Input files can range anywhere from $0200 to $ffff + +IO area and interrupts are disabled during decrunch, and restored before +jumping to start address (default $080d) + +The SFX boot overwrites memory from $00fa to $01ba, and four bytes of stack +($01f3-$1f6). The rest of low memory is undisturbed. + + +Test targets +============ + +make run + - to test the compact decruncher. + Uncomment the TC_BLOCK_INTERFACE line in the Makefile to test the block interface too. + +make frun + - to test the fast decruncher. + Uncomment the TC_BLOCK_INTERFACE line in the Makefile to test the block interface too. + +make runbin + - to test the headerless compact decruncher. + +make frunbin + - to test the headerless fast decruncher. + + +Change Notes +============ + +1.1 + - adds raw (headerless) mode, and warns on dissonant file extensions + +1.1.1 + - ensures test code runs with more versions of cl65 than were originally tested against + - documents the test targets + +1.2 + - added small per-token costs to the cost function to (eg) discourage pairs of two byte copies + wherever a 4 byte copy would provide the same ratio. Improves decrunch speed by 10-20% + + +Acknowledgements +================ + +Thanks to Krill for much assistance getting the small decrunch down to 100 bytes, +as well as general testing and feature requests. + +Thanks to iAN CooG for beta testing the self extracting file functionality. +Thanks to groepaz for assistence with cc65 usage. + +Any remaining bugs are mine. + + diff --git a/loader/tools/tinycrunch_v1.2/tc_boot.prg b/loader/tools/tinycrunch_v1.2/tc_boot.prg new file mode 100644 index 0000000..d7d0e2e Binary files /dev/null and b/loader/tools/tinycrunch_v1.2/tc_boot.prg differ diff --git a/loader/tools/tinycrunch_v1.2/tc_boot.s b/loader/tools/tinycrunch_v1.2/tc_boot.s new file mode 100644 index 0000000..49a6acd --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/tc_boot.s @@ -0,0 +1,38 @@ + +boot_start=$0801 + sei + ldx#decrunch_end-decrunch_dst +: lda decrunch_src-1,x + sta decrunch_dst-1,x + dex + bne :- + jmp decrunch_dst + + +decrunch_src: + .org $0100 +decrunch_dst: + lda 1 + pha + lda#$34 + sta 1 +o_stream0: + lda#0 + ldx#0 + lda decrunch ; replaced with JSR iff chunk present +o_stream1: + lda#0 + ldx#0 + lda decrunch ; replaced with JSR iff chunk present + pla + sta 1 + cli +o_start: + jmp $080d + .include "tc_decode_f.s" +decrunch_end: + ; patch addresses + .byte <(o_stream0 - boot_start + decrunch_src - decrunch_dst) + .byte <(o_stream1 - boot_start + decrunch_src - decrunch_dst) + .byte <(o_start - boot_start + decrunch_src - decrunch_dst) + diff --git a/loader/tools/tinycrunch_v1.2/tc_decode.s b/loader/tools/tinycrunch_v1.2/tc_decode.s new file mode 100644 index 0000000..21b2755 --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/tc_decode.s @@ -0,0 +1,127 @@ + .export decrunch +.ifdef TC_NO_HEADER + .define sp tc_sp + .define dp tc_dp + .exportzp tc_sp, tc_dp +.endif + .define sbx axs + +DECOMPVARS = 4 ; 6 bytes +dp=DECOMPVARS + 0 ; 4 +sp=DECOMPVARS + 2 ; 6 ; sp must follow dp, cf init code +cs=DECOMPVARS + 4 ; 8 + + + +.ifdef TC_BLOCK_INTERFACE + .import tc_getblock +.endif + +.ifdef TC_NO_HEADER +decrunch_done: +.else +decrunch: + stx sp+1 + + ldy#2 +init_loop: + sta dp,y ;first iter stores sp-low :D +.ifdef TC_BLOCK_INTERFACE + ; read three blocks ahead, + ; - one because literal strings read up to 128 bytes past sp + ; - two more to absorb up to 256 blocks worth of read 254 bytes/use 256 bytes + tya + pha + jsr tc_getblock + pla + tay +.endif + lda (sp),y + dey + bpl init_loop + pha + + lda#$02 + bne update_sp + +decrunch_done: + pla + iny + sta(dp),y ; overwrite EOF sentinal for in place decompression +.endif + rts + +literal_run: +literal_loop: + iny + lda(sp),y + sta(dp),y + dex + bmi literal_loop + + tya + pha + clc +increase_dp_by_a_and_sp_by_tos_plus_one: + adc dp + sta dp + bcc :+ + inc dp+1 +: + pla +update_sp: + sec + adc sp + sta sp + bcc :+ + inc sp+1 +.ifdef TC_BLOCK_INTERFACE + jsr tc_getblock +.endif +: + +.ifdef TC_NO_HEADER +decrunch: +.endif +next_command: + ldy#0 + lax(sp),y + beq decrunch_done + ; literal: x = 128+length-1 + ; near copy: a = %11xxxxxx + ; far copy: a|0xf8 = >(~(offset-1)), x = 8*(length-2) | (some low bits) + asl + bcc far_copy + bpl literal_run + +near_copy: + ldx#$07 ; clear high byte of -ve offset. Also ensures copy_loop doesn't loop. + .byt $f0 ; beq (not taken) to skip over the iny +far_copy: + iny + ; carry is set for near_copy, clear for far_copy + + lda(sp),y ;fetch second byte (or for near copy, refetch first). This is low 8 bits of offset. + adc dp + sta cs + txa + ora#$f8 + adc dp+1 + sta cs+1 + tya + pha ; save opcode length to stack + ldy#1 + lda(cs),y + sta(dp),y + +copy_loop: + iny + lda(cs),y + sta(dp),y + txa ; spend an extra 2 cycles per byte here to save 10 in the bitfield extraction. A win on average + sbx#8 + bpl copy_loop + tya + bcc increase_dp_by_a_and_sp_by_tos_plus_one ; always taken. +edecrunch: + diff --git a/loader/tools/tinycrunch_v1.2/tc_decode_f.s b/loader/tools/tinycrunch_v1.2/tc_decode_f.s new file mode 100644 index 0000000..5ebb569 --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/tc_decode_f.s @@ -0,0 +1,176 @@ + .export decrunch +.ifdef TC_NO_HEADER + .define sp tc_sp + .define dp tc_dp + .exportzp tc_sp, tc_dp +.endif + .define sbx axs + +DECOMPVARS = 250 ; 6 bytes +dp=DECOMPVARS + 0 ; 4 +sp=DECOMPVARS + 2 ; 6 ; sp must follow dp, cf init code +cs=DECOMPVARS + 4 ; 8 + + + +.ifdef TC_BLOCK_INTERFACE + .import tc_getblock +.endif + +.ifdef TC_NO_HEADER +decrunch_done: +.else +decrunch: + stx sp+1 + + ldy#2 +init_loop: + sta dp,y ;first iter stores sp-low :D +.ifdef TC_BLOCK_INTERFACE + ; read three blocks ahead, + ; - one because literal strings read up to 128 bytes past sp + ; - two more to absorb up to 256 blocks worth of read 254 bytes/use 256 bytes + tya + pha + jsr tc_getblock + pla + tay +.endif + lda (sp),y + dey + bpl init_loop + pha + + sec + bcs update_sp + +decrunch_done: + pla + iny + sta(dp),y ; overwrite EOF sentinal for in place decompression +.endif + rts + + + +ihidp_fc: + inc dp+1 + clc + bcc update_sp + + + +far_copy: + beq decrunch_done + iny + clc + lda(sp),y + adc dp + sta cs + txa + ora#$f8 + adc dp+1 + sta cs+1 + lda(cs),y + sta(dp),y + +copy_loop: + iny + lda(cs),y + sta(dp),y + txa + sbx#8 + bpl copy_loop + tya + adc dp + sta dp + bcs ihidp_fc +update_sp: + lda#2 + adc sp + sta sp + bcs next_source_page + +.ifdef TC_NO_HEADER +decrunch: +.endif +next_command: + ldy#0 + lax(sp),y + bpl far_copy + asl + bmi near_copy + + +literal_run: +literal_loop: + iny + lda(sp),y + sta(dp),y + dex + bmi literal_loop + + tya + ; carry should be set at this point + adc sp + sta sp + bcc :+ + inc sp+1 +.ifdef TC_BLOCK_INTERFACE + jsr tc_getblock +.endif + clc +: + tya + adc dp + sta dp + bcc :+ + inc dp+1 +: + + ldy#0 + lax(sp),y + bpl far_copy + asl + bpl literal_run + + +near_copy: + ; carry should already be set + txa + adc dp + sta cs + lda#$ff + adc dp+1 + sta cs+1 + iny + lda(cs),y + sta(dp),y + iny + lda(cs),y + sta(dp),y + lda#1 ; carry should be set + adc dp + sta dp + bcs ihidp_nc +incsp_nc: + inc sp + bne next_command +next_source_page: + inc sp+1 +.ifdef TC_BLOCK_INTERFACE + jsr tc_getblock +.endif + jmp next_command + +ihidp_nc: + inc dp+1 + bcs incsp_nc + + + + + + +edecrunch: + diff --git a/loader/tools/tinycrunch_v1.2/tc_encode.py b/loader/tools/tinycrunch_v1.2/tc_encode.py new file mode 100644 index 0000000..e85fb91 --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/tc_encode.py @@ -0,0 +1,635 @@ +#!/usr/bin/env python +from __future__ import division, print_function +from math import log +from collections import defaultdict +import itertools +import os +import sys +boot_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "tc_boot.prg") +__version__ = "1.2.0" + +inf=float('Inf') # importing inf from math only works under python 3 :-/ + + +def load_prg(fi): + data = list(bytearray(fi.read())) + addr = data[0] + 256 * int(data[1]) + return CDataChunk(addr, data[2:]) + +def save_prg(fo, data_chunk): + fo.write(bytearray([data_chunk.addr&255,data_chunk.addr>>8]+data_chunk.data)) + + +class CDataChunk: + def __init__(self, addr, data, se=None): + self.addr=addr + self.data=data + self.se=se # source extent description. Doubles as a marker that this data has been compressed + + @classmethod + def ending_at(cls, ea, data, se=None): + return CDataChunk(ea-len(data), data, se) + + def end_addr(self): + return self.addr+len(self.data) + + def ext(self): + return "0x{:04x}-0x{:04x}".format(self.addr,self.end_addr()) + + def split_at(self,addr): + assert(self.addr<=addr) + assert(addr<=self.end_addr()) + sp=addr-self.addr + lower=CDataChunk(self.addr, self.data[:sp]) + upper=CDataChunk(addr, self.data[sp:]) + return lower,upper + + def extend(self, addend): + assert(self.end_addr()==addend.addr) + self.data.extend(addend.data) + + +class CCruncher: + def __init__(self, greedy=False): + self.longestCopy = 17 + self.maxPairOffset = 63 + self.maxOffset = 2048 + self.longestLiteral = 64 + self.greedy = greedy + self.reset_stats() + + def reset_stats(self): + self.stats = CCruncher.Stats() + + def crunch(self, input_chunk, inPlace=False): + self.inPlace = inPlace + self.output_bytes = [0, 0, 0] # leave space for target address, and final byte + + self.input_chunk = input_chunk + self.data = input_chunk.data[:-1] + self.remainder = input_chunk.data[-1:] + if len(self.data)==0: + return self.output_token_list([]) + + if self.greedy: + return self.crunch_greedy() + else: + return self.crunch_optimal() + + + def encode_literal(self, literal): + "10xxxxxx" + assert (0 < len(literal) <= 64) + return [128 + len(literal) - 1, ] + literal[:] + + def encode_copy(self, length, offset): + assert (1 < length <= self.longestCopy) + if length == 2 and offset <= self.maxPairOffset: + "11oooooo" + assert (offset < 64) + return [(0xc0 + offset) ^ 63, ] # ie, offset^0x7f + else: + "0aaaaabb bbbbbbbb" + # bits 1.5.10 + # assert(2<= length<32) + assert(0> 8) ^ 7) + ((length - 2) << 3), + (offset & 0xff) ^ 0xff + ] + + def crunch_greedy(self): + # just finds for any point the string that extends the furthest + + longestCopy = self.longestCopy + maxOffset = self.maxOffset + tokens = [] + self.literals = [] + + def tokenise_literal_if_present(): + if len(self.literals) > 0: + tokens.append( + self.OptToken(0, self.encode_literal(self.literals), 0, length=len(self.literals), offset=0)) + self.literals = [] + + data = self.data + l = len(data) + nj = 0 + last_seen = defaultdict(lambda: -(2 ** 17)) + for j in range(l): + kl = [tuple(data[j:j + i]) for i in range(longestCopy + 1) if j + i <= l] + + if j == nj: + length = len(kl) - 1 + while length > 1: + offset = j - last_seen[kl[length]] + if offset < maxOffset: + token = self.encode_copy(length, offset) + if token[0] != 0: + tokenise_literal_if_present() + assert (offset > 0) + tokens.append(self.OptToken(0, token, 0, length=length, offset=offset)) + nj = j + length + break + length -= 1 + else: + self.literals.append(data[j]) + if len(self.literals) == self.longestLiteral: + tokenise_literal_if_present() + nj = j + 1 + + for k in kl[1:]: + last_seen[k] = j + tokenise_literal_if_present() + return self.output_token_list(tokens) + + + class OptToken: + def __init__(self, cost, data, next, length, offset): + self.cost = cost + self.data = data + self.next = next + self.length = length + self.offset = offset + + def __repr__(self): + if len(self.data) < 5: + dr = " ".join(['{:02x}'.format(j) for j in self.data]) + else: + dr = "{} bytes".format(len(self.data)) + return "OptToken({},[{}],{},{},{})".format(self.cost, dr, self.next, self.length, self.offset) + + def crunch_optimal(self): + + longestCopy = self.longestCopy + maxPairOffset = self.maxPairOffset + maxOffset = self.maxOffset + longestLiteral = self.longestLiteral + data = self.data + + l = len(data) + last_seen = defaultdict(lambda: -(2 ** 17)) # address key was last seen starting at + + # cfile[j] contains the tail of a list that in turn contains the + # cheapest representation of the first j bytes of the file + # data containst the bytes that must be added to the stream to + # cover the bytes between that token and its predecessor + cfile = [self.OptToken(0, None, None, 0, 0)] + best = None + + for j in range(1, l + 1): + copy_candidates = [tuple(data[j - i:j]) for i in range(2, longestCopy + 1) if + j - i >= 0] # find all the tuples that end at this point + best = self.OptToken(inf, None, None, 0, 0) + + for k in copy_candidates: + mra = last_seen[k] + length = len(k) + start_addr = j - length + assert (length > 1) + offset = j - mra + if offset < maxOffset: + nb = [1.01, 2.012][length > 2 or offset > maxPairOffset] + cost = cfile[start_addr].cost + nb + if cost < best.cost: + token = self.encode_copy(length, offset) + if token[0] != 0: + best = self.OptToken(cost, token, start_addr, length=length, offset=offset) + assert ((mra - length) < j) + + for length in range(1, longestLiteral + 1): + start_addr = j - length + if start_addr >= 0: + cost = cfile[start_addr].cost + length + 1.01 + if cost < best.cost: + literal = data[start_addr:j] + best = self.OptToken(cost, self.encode_literal(literal), start_addr, length=len(literal), + offset=0) + assert (len(best.data) == length + 1) + assert (start_addr < j) + + cfile.append(best) + for k in copy_candidates: + last_seen[k] = j + + assert best is not None + tokens = [best] + while best.next != 0: + best = cfile[best.next] + tokens.append(best) + tokens.reverse() + return self.output_token_list(tokens) + + def output_token_list(self, tokens): + j = len(tokens) + watermark = j # from here on, just store raw data + if self.inPlace: + # Scan the token list from the end back. + # Whenever compressed remainder is equal or longer in length to raw remainder, + # set that token to start of raw data. + raw_bytes_after_token_j = 0 + comp_bytes_after_token_j = 0 + raw_bytes_after_watermark = 0 + while j > 0: + j -= 1 + raw_bytes_after_token_j += tokens[j].length + comp_bytes_after_token_j += len(tokens[j].data) + if raw_bytes_after_token_j <= comp_bytes_after_token_j: + watermark = j + raw_bytes_after_watermark += raw_bytes_after_token_j + raw_bytes_after_token_j = 0 + comp_bytes_after_token_j = 0 + + for t in tokens[:watermark]: + self.stats.log_token(t) + self.output_bytes.extend(itertools.chain.from_iterable(x.data for x in tokens[:watermark])) + if self.inPlace and raw_bytes_after_watermark > 0: + self.remainder = self.data[-raw_bytes_after_watermark:] + self.remainder + if len(self.remainder)>1: + self.stats.log_raw(len(self.remainder)-1) + + self.stats.log_header(3) + self.stats.log_move(1) + self.output_bytes[0] = self.remainder[0] + self.output_bytes[1] = (self.input_chunk.addr - 1) % 256 + self.output_bytes[2] = (self.input_chunk.addr - 1) // 256 + self.stats.log_terminator() + self.remainder[0] = 0 # terminator for compressed data + self.output_bytes.extend(self.remainder) + self.remainder = None + return self.output_bytes + + def report(self, raw=False): + self.stats.report(raw=raw) + + + class Stats: + class StatCounter: + def __init__(self, legend): + self.legend=legend + self.reset() + def reset(self): + self.ct=0 + self.bi=0 + self.bo=0 + def acc(self, ct=1, bi=0, bo=0): + self.ct+=ct + self.bi+=bi + self.bo+=bo + def cost(self): + return max(0,self.bo-self.bi) + def savings(self): + return max(0,self.bi-self.bo) + def print(self, fs, ent, ifp): + l_c=self.ct + l_o=self.bo + l_i=self.bi + if l_c>0: print(fs.format(self.legend, l_c, ent(l_c), ifp(l_i), ifp(l_o - l_i), ifp(l_i-l_o), ifp(l_o), )) + def __init__(self): + self.rs = "" + self.counts={ + 'pair':self.StatCounter('2 byte copies'), + 'copy':self.StatCounter('n byte copies'), + 'literal':self.StatCounter('literal strings'), + 'header':self.StatCounter('segment headers'), + 'move':self.StatCounter('moved to header'), + 'gap':self.StatCounter('gaps'), + 'boot':self.StatCounter('boot'), + 'raw':self.StatCounter('uncompressed'), + 'end':self.StatCounter('terminators'), + 'load':self.StatCounter('load address'), + 'save':self.StatCounter('save address'), + } + self.offsets = [] + self.litlens = [] + + def log_token(self, t): + dl = len(t.data) + if t.offset == 0: + self.log_literal(t.length, dl) + elif dl == 1: + self.log_pair(t.offset, dl) + else: + self.log_copy(t.length, t.offset, dl) + + def log_pair(self, offset, dl): + self.offsets.append(offset) + self.rs += '!' + self.counts['pair'].acc(bi=2, bo=dl) + assert (dl == 1) + + def log_copy(self, length, offset, dl): + self.offsets.append(offset) + self.rs += '@' + self.counts['copy'].acc(bi=length, bo=dl) + assert (dl == 2) + + def log_literal(self, length, dl): + self.litlens.append(length) + self.rs += '.' + assert (dl == 1 + length) + self.counts['literal'].acc(bi=length, bo=dl) + + def log_boot(self, length): self.counts['boot'].acc(bo=length) + def log_gap(self, length): self.counts['gap'].acc(bo=length) + def log_header(self, length): self.counts['header'].acc(bo=length) + def log_move(self, length): self.counts['move'].acc(bi=length) + def log_raw(self, length): self.counts['raw'].acc(bi=length, bo=length) + def log_terminator(self ): self.counts['end'].acc(bo=1) + def log_load_addr(self ): self.counts['load'].acc(bi=2,ct=1) + def log_save_addr(self ): self.counts['save'].acc(bo=2) + + def report(self, raw=False): + # print(self.rs) + g1= "copy pair literal end".split() + g2= "move load save boot header gap raw".split() + + if raw: + for k in g2: self.counts[k].reset() + + symcount = sum(self.counts[k].ct for k in g1) + vi=self.counts.values + + s_c = sum(c.ct for c in vi()) + s_i = sum(c.bi for c in vi()) + cost = sum(c.cost() for c in vi()) + savings = sum(c.savings() for c in vi()) + s_o = sum(c.bo for c in vi()) + assert (s_i + cost - savings == s_o) + + ent = lambda x: ["n/a", "{:7.3f}".format(log((x + 1e-20) / (symcount + 1e-20)) / log(0.5))][x > 0] + noent= lambda x: "" + ifp = lambda x: ["", x][x > 0] + hr = "+-----------------+------------------+----------------------------------+" + fs = "|{:>16} | {:>7} {:>7} | {:>7} {:>7} {:>7} {:>7} |" + print() + print(hr) + print(fs.format("", "count", "entropy", "input", "cost", "savings", "output", )) + print(hr) + for k in g1: self.counts[k].print(fs, ent, ifp) + print(hr) + if not raw: + for k in g2: self.counts[k].print(fs, noent, ifp) + print(hr) + print(fs.format("total", s_c, "", s_i, cost, savings, s_o, )) + print(hr) + + + self.offsets.sort() + if len(self.offsets): + print("median, maximum offset used = {:}, {:}".format(self.offsets[len(self.offsets) // 2], self.offsets[-1])) + self.litlens.sort() + if len(self.litlens): + print("median, maximum litlen used = {:}, {:}".format(self.litlens[len(self.litlens) // 2], + self.litlens[-5:])) + print() + + +if __name__ == "__main__": + import argparse + + def hi(x): + return x//256 + def lo(x): + return x&255 + def parse_args(): + def hex(x): + return x and int(x, 16) + + parser = argparse.ArgumentParser(prog='python '+os.path.basename(sys.argv[0])) + parser.add_argument('-V', '--version', action='version', + version='%(prog)s {version}'.format(version=__version__)) + parser.add_argument('infile', type=argparse.FileType('rb'), help="(.prg file unless -r is used)") + parser.add_argument('outfile', type=argparse.FileType('wb'), help="(.prg file unless -r is used)") + + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("-s", "--startAddress", dest="startAddr", help="start address", default=None, type=hex) + group.add_argument("-e", "--endAddress", dest="endAddr", help="end address", default=None, type=hex) + group.add_argument("-i", "--inPlace", dest="inPlace", help="compress to end of destination area", + action="store_true") + group.add_argument("-x", "--selfExtracting", action="store_true") + group.add_argument("-r", "--raw", action="store_true", help="read/write .bin files, no header. cf readme.txt") + parser.add_argument("-j", "--jmp", dest="execAddr", help="execution address for self extracting .prg (requires -x)", default=0x080d, type=hex) + + parser.add_argument("-p", "--paramFile", type=argparse.FileType('w'), default=None, help="generated asm include file containing a define for the output start address") + parser.add_argument("-v", "--verbose", action="store_true") + parser.add_argument("-f", "--fast", action="store_true", + help="faster (greedy) compression (default is optimal size)") + return parser.parse_args() + + def level_crunch(args, op, input_chunk): + output_bytes = op.crunch(input_chunk, args.inPlace) + + if args.inPlace: + la = input_chunk.end_addr() - len(output_bytes) + elif args.endAddr is not None: + la = args.endAddr - len(output_bytes) + else: + la = args.startAddr + + output_chunk=CDataChunk(la, output_bytes) + + if args.paramFile: + print("dcSrc=$%04x" % (output_chunk.addr,), file=args.paramFile) + args.paramFile.close() + + return output_chunk + + + def raw_crunch(args, op, input_data): + assert not args.inPlace, "--inPlace makes no sense for raw mode" + assert not args.paramFile, "cannot generate paramFile for raw mode" + input_chunk = CDataChunk(0, input_data+[0,]) #add fake load address, and dummy byte-to-move-to-header + output_bytes = op.crunch(input_chunk, False) + return output_bytes[3:] #drop header + + + def sfx_crunch(args, op, input_chunk): + """ + compresses the input file in two segments. + The first contains a compressed copy of the data + to be decrunched to the area after the loaded file + The second contains the remaining data, + compressed in place. + + It takes an iteration or three to find the optimal split point + """ + + def dprint(*prargs): + if args.verbose: + print(*prargs) + + def disp_chunks(chunks): + for chunk in chunks: + if chunk.se is None: + dprint("data segment at {}, uncompressed".format(chunk.ext())) + else: + dprint("data segment at {}, decrunches to {}".format(chunk.ext(), chunk.se)) + + dprint() + dprint(" input at {}".format( input_chunk.ext())) + + boot_chunk = load_prg(open(boot_path,'rb')) + output_chunk, offsets=boot_chunk.split_at(boot_chunk.end_addr()-3) + + patch_offsets = offsets.data + o_start = patch_offsets[2] + + dprint(" boot at {}".format(output_chunk.ext())) + + data_start = output_chunk.end_addr() + monolith=CDataChunk(data_start, op.crunch(input_chunk, False), input_chunk.ext()) + monolith_stats=op.stats + + dprint(" monolith at {}".format( monolith.ext())) + + if input_chunk.addr>=monolith.end_addr() or input_chunk.end_addr()<=monolith.addr: + dprint("(doesn't overlap output, using as is)") + chunks=[monolith,] #this is safe because it doesn't overlap the input chunk + else: + split_point=min(monolith.end_addr()+12, input_chunk.end_addr()-1) #assume compression is slightly worse + max_gap=len(input_chunk.data)//2000 # try for 0.05% bytes wasted between output segments + while 1: + op.reset_stats() + if split_point>=input_chunk.end_addr(): + dprint("\nnew split point of 0x{:04x} is past end of input.".format(split_point)) + split_point = data_start + + if input_chunk.addr safe + ] + + gap=chunks[1].addr-chunks[0].end_addr() + if gap<0: + adjustment = -gap + adjustment -= adjustment//5 # reduce larger steps a little + disp_chunks(chunks) + dprint("segment overlap = {}".format(-gap)) + dprint("shifting split up by {} bytes and recrunching".format(adjustment)) + split_point+=adjustment + continue + disp_chunks(chunks) + dprint("segment gap = {} (max={})".format(gap, max_gap)) + if gap>max_gap: + adjustment=gap-gap//4 + dprint("shifting split down by {} bytes and recrunching".format(adjustment)) + split_point-=adjustment + max_gap+=1+max_gap # increase tolerance to escape oscillation. + else: + """ + ok. At this point, + gap >= 0 + chunks[1].addr-chunks[0].end_addr() >= 0 + chunks[1].addr >= chunks[0].end_addr() + chunks[1].end_addr() >= chunks[0].end_addr() (as c1.end_addr>=c1.addr) + split_point >= chunks[0].end_addr() + upper_chunk.addr >= chunks[0].end_addr() + + therefore, chunk 0 is safe. + """ + dprint("close enough." if gap>0 else "perfect.") + break + + op.stats.log_boot(len(output_chunk.data)) + + for chunk,offset in zip(chunks,patch_offsets[:2]): + gap = chunk.addr-output_chunk.end_addr() + if gap>0: + op.stats.log_gap(gap) + output_chunk.data.extend([0xff,]*gap) + + if chunk.se is not None: + output_chunk.data[offset+1]=lo(chunk.addr) + output_chunk.data[offset+3]=hi(chunk.addr) + output_chunk.data[offset+4]=0x20 # replace LDA decrunch with JSR decrunch + else: + op.stats.log_raw(len(chunk.data)) + output_chunk.extend(chunk) + + exec_addr=args.execAddr + output_chunk.data[o_start+1]=lo(exec_addr) + output_chunk.data[o_start+2]=hi(exec_addr) + + return output_chunk + + + args = parse_args(); + op = CCruncher(greedy=args.fast) + + if args.raw: + if args.infile.name.endswith('.prg'): + print("warning, input file will be parsed as a .bin", file=sys.stderr) + if args.outfile.name.endswith('.prg'): + print("warning, output file will be written as a .bin", file=sys.stderr) + input_data=list(bytearray(args.infile.read())) + original_length = len(input_data) + print("{:5d} bytes read from {}".format( + original_length, args.infile.name)) + output_bytes = raw_crunch(args, op, input_data) + + if args.verbose: + op.report(raw=True) + + args.outfile.write(bytearray(output_bytes)) + compressed_length = len(output_bytes) + print("{:5d} bytes written to {} ({:4.1f}% of original size)".format( + compressed_length, args.outfile.name, + compressed_length * 100.0 / original_length) + ) + else: + if args.infile.name.endswith('.bin'): + print("warning, input file will be parsed as a .prg", file=sys.stderr) + if args.outfile.name.endswith('.bin'): + print("warning, output file will be written as a .prg", file=sys.stderr) + input_chunk=load_prg(args.infile) + original_length = len(input_chunk.data) + 2 + print("{}: {:5d} bytes read from {}".format( + input_chunk.ext(), original_length, args.infile.name)) + + if len(input_chunk.data)<1: + print("Input file can't be empty", file=sys.stderr) + exit(1) + + if args.selfExtracting: + if input_chunk.addr<0x0200: + print(input_chunk.addr) + print("Destination addresses below 0x0200 not supported by -x", file=sys.stderr) + exit(1) + output_chunk = sfx_crunch(args, op, input_chunk) + else: + output_chunk = level_crunch(args, op, input_chunk) + + op.stats.log_load_addr() + op.stats.log_save_addr() + if args.verbose: + op.report() + + save_prg(args.outfile, output_chunk) + compressed_length = len(output_chunk.data) + 2 + + print("{}: {:5d} bytes written to {} ({:4.1f}% of original size)".format( + output_chunk.ext(), compressed_length, args.outfile.name, + compressed_length * 100.0 / original_length) + ) + diff --git a/loader/tools/tinycrunch_v1.2/test/cbmcat b/loader/tools/tinycrunch_v1.2/test/cbmcat new file mode 100644 index 0000000..46795f7 --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/test/cbmcat @@ -0,0 +1,45 @@ +#!/usr/bin/env python +from __future__ import print_function +import sys +from struct import pack,unpack + +if sys.platform == "win32": + import os, msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + +class Mem: + def __init__(self): + self.low_water=0xffff + self.high_water=0 + page=b'\0'*256 + self.mem=page*256 + def load_prg(self, fn): + data=open(fn,'rb').read() + start=unpack('dcSrc + jsr decrunch + + lda#11 + sta $d020 + + lda#$3b + sta $d011 + lda#$18 + sta $d018 +go: + jmp go + + +tc_getblock: + ldy $d020 + lda#8 + sta $d020 + ldx#0 +@s: lda #255 +@i: eor dcSrc,x +@o: sta dcSrc,x + inc $d020 + dec $d020 + inx + bne @s + lda @i+2 + cmp#$42 + beq noi + inc @i+2 + inc @o+2 +noi: + sty $d020 + rts + +hide_the_test_data: + ldx#0 +@s: lda#255 +@i: eor dcSrc,x +@o: sta dcSrc,x + inx + bne @s + inc @i+2 + inc @o+2 + lda @o+2 + cmp#$42 + bne @s + rts + diff --git a/loader/tools/tinycrunch_v1.2/test/testbin.s b/loader/tools/tinycrunch_v1.2/test/testbin.s new file mode 100644 index 0000000..86d7dfd --- /dev/null +++ b/loader/tools/tinycrunch_v1.2/test/testbin.s @@ -0,0 +1,38 @@ + .import decrunch + .importzp tc_sp, tc_dp +bmp = $2000 + + jsr initscreen + + lda#<(bmp-1) + sta tc_dp + lda#>(bmp-1) + sta tc_dp+1 + + lda#dcSrc + sta tc_sp+1 + + jsr decrunch + +done: + jmp done + + + + +initscreen: + lda#11 + sta $d021 + jsr $e536 + lda#$56 + sta $07e7 + lda#$3b + sta $d011 + lda#$18 + sta $d018 + rts + +dcSrc: + .incbin "../crunched.bin" diff --git a/loader/tools/tscrunch/decrunch.asm b/loader/tools/tscrunch/decrunch.asm new file mode 100644 index 0000000..8535e41 --- /dev/null +++ b/loader/tools/tscrunch/decrunch.asm @@ -0,0 +1,293 @@ +/* + +decrunch.asm + +NMOS 6502 decompressor for data stored in TSCrunch format. + +This code is written for the KickAssembler assembler. + +Copyright Antonio Savona 2022. + +*/ + + +//#define INPLACE //Enables inplace decrunching. Use -i switch when crunching. + +.label tsget = $f8 //2 bytes +.label tstemp = $fa +.label tsput = $fb //2 bytes +.label lzput = $fd //2 bytes + + +#if INPLACE + +.macro TS_DECRUNCH(src) +{ + lda #src + sta.zp tsget + 1 + jsr tsdecrunch +} + +#else + +.macro TS_DECRUNCH(src,dst) +{ + lda #src + sta.zp tsget + 1 + lda #dst + sta.zp tsput + 1 + jsr tsdecrunch +} + +#endif + + +tsdecrunch: +{ + decrunch: + + #if INPLACE + ldy #$ff + !: iny + lda (tsget),y + sta tsput , y //last iteration trashes lzput, with no effect. + cpy #3 + bne !- + + pha + + lda lzput + sta optRun + 1 + + tya + ldy #0 + beq update_getonly + #else + ldy #0 + + + lda (tsget),y + sta optRun + 1 + + inc tsget + bne entry2 + inc tsget + 1 + #endif + + entry2: + lax (tsget),y + + bmi rleorlz + + cmp #$20 + bcs lz2 + + //literal + + #if INPLACE + + inc tsget + beq updatelit_hi + return_from_updatelit: + + ts_delit_loop: + + lda (tsget),y + sta (tsput),y + iny + dex + + bne ts_delit_loop + + tya + tax + //carry is clear + ldy #0 + #else //not inplace + tay + + ts_delit_loop: + + lda (tsget),y + dey + sta (tsput),y + + bne ts_delit_loop + + txa + inx + #endif + + updatezp_noclc: + adc tsput + sta tsput + bcs updateput_hi + putnoof: + txa + update_getonly: + adc tsget + sta tsget + bcc entry2 + inc tsget+1 + bcs entry2 + + #if INPLACE + updatelit_hi: + inc tsget+1 + bcc return_from_updatelit + #endif + updateput_hi: + inc tsput+1 + clc + bcc putnoof + + rleorlz: + + alr #$7f + bcc ts_delz + + //RLE + beq optRun + + plain: + ldx #2 + iny + sta tstemp //number of bytes to de-rle + + lda (tsget),y //fetch rle byte + ldy tstemp + !runStart: + sta (tsput),y + + ts_derle_loop: + + dey + sta (tsput),y + + bne ts_derle_loop + + //update zero page with a = runlen, x = 2 , y = 0 + lda tstemp + + bcs updatezp_noclc + + done: +#if INPLACE + pla + sta (tsput),y +#endif + rts + //LZ2 + lz2: + beq done + + ora #$80 + adc tsput + sta lzput + lda tsput + 1 + sbc #$00 + sta lzput + 1 + + //y already zero + lda (lzput),y + sta (tsput),y + iny + lda (lzput),y + sta (tsput),y + + tya + dey + + adc tsput + sta tsput + bcs lz2_put_hi + !skp: + inc tsget + bne entry2 + inc tsget + 1 + bne entry2 + + lz2_put_hi: + inc tsput + 1 + bcs !skp- + + //LZ + ts_delz: + + lsr + sta lzto + 1 + + iny + + lda tsput + bcc long + + sbc (tsget),y + sta lzput + lda tsput+1 + + sbc #$00 + + ldx #2 + //lz MUST decrunch forward + lz_put: + sta lzput+1 + + ldy #0 + + lda (lzput),y + sta (tsput),y + + iny + lda (lzput),y + sta (tsput),y + + ts_delz_loop: + + iny + + lda (lzput),y + sta (tsput),y + + lzto: cpy #0 + bne ts_delz_loop + + tya + + //update zero page with a = runlen, x = 2, y = 0 + ldy #0 + //clc not needed as we have len - 1 in A (from the encoder) and C = 1 + + jmp updatezp_noclc + + optRun: + ldy #255 + sty tstemp + + ldx #1 + //A is zero + + bne !runStart- + + long: + //carry is clear and compensated for from the encoder + adc (tsget),y + sta lzput + iny + lax (tsget),y + ora #$80 + adc tsput + 1 + + cpx #$80 + rol lzto + 1 + ldx #3 + + bne lz_put + +} \ No newline at end of file diff --git a/loader/tools/tscrunch/decrunch_extreme.asm b/loader/tools/tscrunch/decrunch_extreme.asm new file mode 100644 index 0000000..10f5c62 --- /dev/null +++ b/loader/tools/tscrunch/decrunch_extreme.asm @@ -0,0 +1,342 @@ +/* + +decrunch_extreme.asm + +NMOS 6502 decompressor for data stored in TSCrunch format. + +This code is written for the KickAssembler assembler. + +Copyright Antonio Savona 2022. + +*/ + + +//#define INPLACE //Enables inplace decrunching. Use -i switch when crunching. + +.label tsget = $f8 //2 bytes +.label tstemp = $fa +.label tsput = $fb //2 bytes +.label lzput = $fd //2 bytes + + +#if INPLACE + +.macro TS_DECRUNCH(src) +{ + lda #src + sta.zp tsget + 1 + jsr tsdecrunch +} + +#else + +.macro TS_DECRUNCH(src,dst) +{ + lda #src + sta.zp tsget + 1 + lda #dst + sta.zp tsput + 1 + jsr tsdecrunch +} + +#endif + + +tsdecrunch: +{ + decrunch: + + #if INPLACE + ldy #$ff + !: iny + lda (tsget),y + sta tsput , y //last iteration trashes lzput, with no effect. + cpy #3 + bne !- + + pha + + lda lzput + sta optRun + 1 + + ldx #$d0 //bne opcode + and #1 + bne !skp+ + ldx #$29 //and immediate opcode + !skp: + stx optOdd + + tya + ldy #0 + beq update_getonly + #else + ldy #0 + + lda (tsget),y + sta optRun + 1 + + ldx #$d0 //bne opcode + and #1 + bne !skp+ + ldx #$29 //and immediate opcode + !skp: + stx optOdd + + inc tsget + bne entry2 + inc tsget + 1 + #endif + + entry2: + lax (tsget),y + + bmi rleorlz + + cmp #$20 + bcs lz2 + //literal + + #if INPLACE + + inc tsget + beq updatelit_hi + return_from_updatelit: + and #1 + bne !odd+ + + ts_delit_loop: + + lda (tsget),y + sta (tsput),y + iny + dex + !odd: + lda (tsget),y + sta (tsput),y + iny + dex + + bne ts_delit_loop + + tya + tax + //carry is clear + ldy #0 + #else //not inplace + tay + + and #1 + bne !odd+ + + ts_delit_loop: + + lda (tsget),y + dey + sta (tsput),y + !odd: + lda (tsget),y + dey + sta (tsput),y + + bne ts_delit_loop + + txa + inx + #endif + + updatezp_noclc: + adc tsput + sta tsput + bcs updateput_hi + putnoof: + txa + update_getonly: + adc tsget + sta tsget + bcc entry2 + inc tsget+1 + bcs entry2 + + #if INPLACE + updatelit_hi: + inc tsget+1 + bcc return_from_updatelit + #endif + updateput_hi: + inc tsput+1 + clc + bcc putnoof + + //LZ2 + lz2: + beq done + + ora #$80 + adc tsput + sta lzput + lda tsput + 1 + sbc #$00 + sta lzput + 1 + + //y already zero + lda (lzput),y + sta (tsput),y + iny + lda (lzput),y + sta (tsput),y + + tya + dey + + adc tsput + sta tsput + bcs lz2_put_hi + !skp: + inc tsget + bne entry2 + inc tsget + 1 + bne entry2 + + lz2_put_hi: + inc tsput + 1 + bcs !skp- + + rleorlz: + + alr #$7f + bcc ts_delz + + //RLE + beq zeroRun + + plain: + + iny + sta tstemp //number of bytes to de-rle + + lsr //c = test parity + + lda (tsget),y //fetch rle byte + ldy tstemp + runStart: + sta (tsput),y + + bcs !odd+ + sec + + ts_derle_loop: + dey + sta (tsput),y + !odd: + + dey + sta (tsput),y + + bne ts_derle_loop + + //update zero page with a = runlen, x = 2 , y = 0 + lda tstemp + ldx #2 + bcs updatezp_noclc + + + done: +#if INPLACE + pla + sta (tsput),y +#endif + rts + + + //LZ + ts_delz: + + lsr + sta lzto + 1 + + iny + + lda tsput + bcc long + + sbc (tsget),y + sta lzput + lda tsput+1 + + sbc #$00 + + ldx #2 + //lz MUST decrunch forward + lz_put: + sta lzput+1 + + ldy #0 + + lda lzto + 1 + lsr + bcs !odd+ + + lda (lzput),y + sta (tsput),y + ts_delz_loop: + iny + + !odd: + + lda (lzput),y + sta (tsput),y + + iny + + lda (lzput),y + sta (tsput),y + + lzto: cpy #0 + bne ts_delz_loop + + tya + + //update zero page with a = runlen, x = 2, y = 0 + ldy #0 + //clc not needed as we have len - 1 in A (from the encoder) and C = 1 + jmp updatezp_noclc + + zeroRun: + optRun: ldy #255 + sta (tsput),y + optOdd: bne !odd+ + ts_dezero_loop: + dey + sta (tsput),y + !odd: + dey + sta (tsput),y + bne ts_dezero_loop + + lda optRun + 1 + + ldx #1 + jmp updatezp_noclc + + long: + //carry is clear and compensated for from the encoder + adc (tsget),y + sta lzput + iny + lax (tsget),y + ora #$80 + adc tsput + 1 + + cpx #$80 + rol lzto + 1 + ldx #3 + + bne lz_put + +} \ No newline at end of file diff --git a/loader/tools/tscrunch/decrunch_small.asm b/loader/tools/tscrunch/decrunch_small.asm new file mode 100644 index 0000000..33b3503 --- /dev/null +++ b/loader/tools/tscrunch/decrunch_small.asm @@ -0,0 +1,280 @@ +/* + +decrunch.asm + +NMOS 6502 decompressor for data stored in TSCrunch format. + +This code is written for the KickAssembler assembler. + +Copyright Antonio Savona 2022. + +*/ + + +//#define INPLACE //Enables inplace decrunching. Use -i switch when crunching. + +.label tsget = $f8 //2 bytes +.label tstemp = $fa +.label tsput = $fb //2 bytes +.label lzput = $fd //2 bytes + + +#if INPLACE + +.macro TS_DECRUNCH(src) +{ + lda #src + sta.zp tsget + 1 + jsr tsdecrunch +} + +#else + +.macro TS_DECRUNCH(src,dst) +{ + lda #src + sta.zp tsget + 1 + lda #dst + sta.zp tsput + 1 + jsr tsdecrunch +} + +#endif + + +tsdecrunch: +{ + decrunch: + + #if INPLACE + ldy #$ff + !: iny + lda (tsget),y + sta tsput , y //last iteration trashes lzput, with no effect. + cpy #3 + bne !- + + pha + + lda lzput + sta optRun + 1 + + tya + ldy #0 + beq update_getonly + #else + ldy #0 + + lda (tsget),y + sta optRun + 1 + + inc tsget + bne entry2 + inc tsget + 1 + #endif + + entry2: + lax (tsget),y + + bmi rleorlz + + cmp #$20 + bcs lz2 + + //literal + + #if INPLACE + + inc tsget + beq updatelit_hi + return_from_updatelit: + + ts_delit_loop: + + lda (tsget),y + sta (tsput),y + iny + dex + + bne ts_delit_loop + + tya + tax + //carry is clear + ldy #0 + #else //not inplace + tay + + ts_delit_loop: + + lda (tsget),y + dey + sta (tsput),y + + bne ts_delit_loop + + txa + inx + #endif + + updatezp_noclc: + adc tsput + sta tsput + bcs updateput_hi + putnoof: + txa + update_getonly: + adc tsget + sta tsget + bcc entry2 + inc tsget+1 + bcs entry2 + + #if INPLACE + updatelit_hi: + inc tsget+1 + bcc return_from_updatelit + #endif + updateput_hi: + inc tsput+1 + clc + bcc putnoof + + + rleorlz: + alr #$7f + bcc ts_delz + + //RLE + beq optRun + + plain: + ldx #2 + iny + sta tstemp //number of bytes to de-rle + + lda (tsget),y //fetch rle byte + ldy tstemp + !runStart: + sta (tsput),y + + ts_derle_loop: + + dey + sta (tsput),y + + bne ts_derle_loop + + //update zero page with a = runlen, x = 2 , y = 0 + lda tstemp + + bcs updatezp_noclc + + done: +#if INPLACE + pla + sta (tsput),y +#endif + rts + //LZ2 + lz2: + beq done + + ora #$80 + adc tsput + sta lzput + lda tsput + 1 + sbc #$00 + sta lzput + 1 + + //y already zero + lda (lzput),y + sta (tsput),y + iny + lda (lzput),y + sta (tsput),y + + tya //y = a = 1. + tax //y = a = x = 1. a + carry = 2 + dey //ldy #0 + + beq updatezp_noclc + + //LZ + ts_delz: + + lsr + sta lzto + 1 + + iny + + lda tsput + bcc long + + sbc (tsget),y + sta lzput + lda tsput+1 + + sbc #$00 + + ldx #2 + //lz MUST decrunch forward + lz_put: + sta lzput+1 + + ldy #0 + + lda (lzput),y + sta (tsput),y + + ts_delz_loop: + + iny + + lda (lzput),y + sta (tsput),y + + lzto: cpy #0 + bne ts_delz_loop + + tya + + //update zero page with a = runlen, x = 2, y = 0 + ldy #0 + //clc not needed as we have len - 1 in A (from the encoder) and C = 1 + #if INPLACE + jmp updatezp_noclc + #else + bcs updatezp_noclc + #endif + + optRun: + ldy #255 + sty tstemp + + ldx #1 + //A is zero + bne !runStart- + + long: + //carry is clear and compensated for from the encoder + adc (tsget),y + sta lzput + iny + lax (tsget),y + ora #$80 + adc tsput + 1 + + cpx #$80 + rol lzto + 1 + ldx #3 + + bne lz_put + +} \ No newline at end of file diff --git a/loader/tools/tscrunch/readme.txt b/loader/tools/tscrunch/readme.txt new file mode 100644 index 0000000..aab0f3a --- /dev/null +++ b/loader/tools/tscrunch/readme.txt @@ -0,0 +1,117 @@ +TSCrunch V1.3 + +by Antonio Savona + +April 2022 + + +About +===== + +TSCrunch is an optimal, byte-aligned, LZ+RLE hybrid encoder, designed to maximize decoding speed on NMOS 6502 and derived CPUs, while keeping decent compression performance (for a bytecruncher, that is). +TSCrunch was designed as the default asset cruncher for the upcoming game A Pig Quest, and, as such, it's optimized for in-memory level compression, but at as of version 1.0 it can also create SFX executables for off-line prg crunching. + +Requirements +============ + +TSCrunch requires python 3.x with scipy library installed, or a windows x64 machine(A pre-compiled windows x64 command line executable is also provided). A GO version is also supplied to facilitate building on your OS of choice. +The memory decrunchers requires Kick Assembler, but it should be quite easy to port it to your assembler of choice. + +Usage +===== + +tscrunch [option] infile outfile + +Crunching examples: + + tscrunch -x $0820 game.prg crunched.prg + +Crunches the file game.prg and generates a self executable crunched.prg, using $0820 as post-decrunch jmp address + + tscrunch -p game.prg crunched.bin + +Mem-crunches the file game.prg, stripping the 2-byte header and generates a binary file crunched.bin + + tscrunch data.bin crunched.bin +Mem-crunches the file data.bin and generates a binary file crunched.bin + + tscrunch -i data.prg crunched.prg +Mem-crunches the file data.prg for in-place use, and generates a prg file crunched.prg with the appropriate load address + + +Please refer to the inline help (tscrunch -h) for a detailed description of the different crunching options. +Note that with the exception of self executables and in-place, all the files generated by TSCrunch are headless binaries, that is they don't come with a 2 byte loader offset. + +Decrunching files from code +=========================== + +For memory decrunching, please #include decrunch.asm and include the crunched binaries in your code, then use the macro TS_DECRUNCH, as explained by the following code fragment + + .pc = $1000 "test" + //decrunches data to $4000 + :TS_DECRUNCH(compressed_data,$4000) + jmp * + + .align $100 + #include "decrunch.asm" + + compressed_data: + .import binary "data.bin" + + +For inplace decrunching, please #define INPLACE before including the decruncher code, as explained by the following code fragment + + #define INPLACE + + .pc = $1000 "test" + //decrunches data inplace + :TS_DECRUNCH(compressed_data) + jmp * + + .align $100 + #include "decrunch.asm" + + .pc = LoadAddress //as provided by the cruncher + compressed_data: + .import c64 "data.bin" + + +decruncher.asm is the recommended decruncher for the general case, but other than it two alternative decrunchers are supplied: a small version, which saves some bytes at the cost of speed, and an extreme version which is generally marginally faster, but comes with a larger footprint. + + +Performance +=========== + +TSCrunch is designed for ultra-fast decrunching while keeping a decent compression ratio. Being a byte-cruncher, it falls short of popular bit-crunchers, such as exomizer or B2, when comparing compression efficiency, but it is usually much faster at decoding. Furthermore, you can expect a 20% to 40% speed bump compared to popular byte-crunchers with similar compression efficiency. +The following benchmark compares TSCrunch performance with those of a fast byte-cruncher, TinyCrunch, and a fast bit-cruncher, B2, on a real-case compression scenario: Chopper Command, from the same author. + + +Chopper Command - Raw encoding - game prg + + Tscrunch 1.3 TinyCrunch 1.2 B2 +Size 46913 46913 46913 +Crunched size 12506 15419 11181 +% of original 26.66% 32.87% 23.83% +Decrunch cycles 754733 1133039 1694585 +Cycles per byte 16.08792872 24.15191951 36.12186388 + + +Changelog +========= + +1.3 +-Improved compression adding near-optimal zero-runs and refactoring literals and lz2 tokens +-Improved decrunching speed +-Added extreme and small decruncher versions, for maximum speed and minimal footprint respectively + +1.2 +-Added long matches to improve compression with no effect on decrunching speed +-Fixed bug in LZ2 search that would prevent some short matches from being identified +-Code is available also in Go to improve crunching speed and increase portability + +1.1 +-Added Inplace compression +-Minor speed improvement + +1.0 +-Initial release \ No newline at end of file diff --git a/loader/tools/tscrunch/tscrunch.go b/loader/tools/tscrunch/tscrunch.go new file mode 100644 index 0000000..6e7eaa6 --- /dev/null +++ b/loader/tools/tscrunch/tscrunch.go @@ -0,0 +1,650 @@ +/* +TSCrunch binary cruncher, by Antonio Savona +*/ + +package main + +import ( + "bytes" + "dijkstra" //go get github.com/RyanCarrier/dijkstra + "flag" + "fmt" + "math" + "os" + "sort" + "strconv" + "sync" +) + +type crunchCtx struct { + QUIET bool + PRG bool + SFX bool + INPLACE bool + jmp uint16 + decrunchTo uint16 + loadTo uint16 + addr []byte +} + +type edge struct { + n0 int + n1 int +} + +type token struct { + tokentype byte + size int + rlebyte byte + offset int + i int +} + +const LONGESTRLE = 64 +const LONGESTLONGLZ = 64 +const LONGESTLZ = 32 +const LONGESTLITERAL = 31 +const MINRLE = 2 +const MINLZ = 3 +const LZOFFSET = 32767 +const LZ2OFFSET = 94 + +const RLEMASK = 0x81 +const LZMASK = 0x80 +const LITERALMASK = 0x00 +const LZ2MASK = 0x00 + +const TERMINATOR = LONGESTLITERAL + 1 + +const LZ2ID = 3 +const LZID = 2 +const RLEID = 1 +const LITERALID = 4 +const LONGLZID = 5 +const ZERORUNID = 6 + +var boot = []byte{ + + 0x01, 0x08, 0x0B, 0x08, 0x0A, 0x00, 0x9E, 0x32, 0x30, 0x36, 0x31, 0x00, + 0x00, 0x00, 0x78, 0xA2, 0xC9, 0xBD, 0x1A, 0x08, 0x95, 0x00, 0xCA, 0xD0, + 0xF8, 0x4C, 0x02, 0x00, 0x34, 0xBD, 0x00, 0x10, 0x9D, 0x00, 0xFF, 0xE8, + 0xD0, 0xF7, 0xC6, 0x04, 0xC6, 0x07, 0xA5, 0x04, 0xC9, 0x07, 0xB0, 0xED, + 0xA0, 0x00, 0xB3, 0x21, 0x30, 0x21, 0xC9, 0x20, 0xB0, 0x3F, 0xA8, 0xB9, + 0xFF, 0xFF, 0x88, 0x99, 0xFF, 0xFF, 0xD0, 0xF7, 0x8A, 0xE8, 0x65, 0x25, + 0x85, 0x25, 0xB0, 0x77, 0x8A, 0x65, 0x21, 0x85, 0x21, 0x90, 0xDF, 0xE6, + 0x22, 0xB0, 0xDB, 0x4B, 0x7F, 0x90, 0x3A, 0xF0, 0x6B, 0xA2, 0x02, 0x85, + 0x53, 0xC8, 0xB1, 0x21, 0xA4, 0x53, 0x91, 0x25, 0x88, 0x91, 0x25, 0xD0, + 0xFB, 0xA9, 0x00, 0xB0, 0xD5, 0xA9, 0x37, 0x85, 0x01, 0x58, 0x4C, 0x5B, + 0x00, 0xF0, 0xF6, 0x09, 0x80, 0x65, 0x25, 0x85, 0x9B, 0xA5, 0x26, 0xE9, + 0x00, 0x85, 0x9C, 0xB1, 0x9B, 0x91, 0x25, 0xC8, 0xB1, 0x9B, 0x91, 0x25, + 0x98, 0xAA, 0x88, 0xF0, 0xB1, 0x4A, 0x85, 0xA0, 0xC8, 0xA5, 0x25, 0x90, + 0x33, 0xF1, 0x21, 0x85, 0x9B, 0xA5, 0x26, 0xE9, 0x00, 0x85, 0x9C, 0xA2, + 0x02, 0xA0, 0x00, 0xB1, 0x9B, 0x91, 0x25, 0xC8, 0xB1, 0x9B, 0x91, 0x25, + 0xC8, 0xB9, 0x9B, 0x00, 0x91, 0x25, 0xC0, 0x00, 0xD0, 0xF6, 0x98, 0xA0, + 0x00, 0xB0, 0x83, 0xE6, 0x26, 0x18, 0x90, 0x84, 0xA0, 0xFF, 0x84, 0x53, + 0xA2, 0x01, 0xD0, 0x96, 0x71, 0x21, 0x85, 0x9B, 0xC8, 0xB3, 0x21, 0x09, + 0x80, 0x65, 0x26, 0x85, 0x9C, 0xE0, 0x80, 0x26, 0xA0, 0xA2, 0x03, 0xD0, + 0xC4, +} + +var wg sync.WaitGroup +var mg, ms, me sync.Mutex + +var starts = make(map[int]bool) +var ends = make(map[int]bool) +var graph = make(map[edge]token) + +var optimalRun int = 0 + +func usage() { + fmt.Println("TSCrunch 1.3 - binary cruncher, by Antonio Savona") + fmt.Println("Usage: tscrunch [-p] [-i] [-q] [-x $addr] infile outfile") + fmt.Println(" -p : input file is a prg, first 2 bytes are discarded.") + fmt.Println(" -x $addr: creates a self extracting file (forces -p)") + fmt.Println(" -i : inplace crunching (forces -p)") + fmt.Println(" -q : quiet mode") +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +func load_raw(f string) []byte { + data, err := os.ReadFile(f) + if err == nil { + return data + } else { + fmt.Println("can't read data") + return nil + } +} + +func save_raw(f string, data []byte) { + os.WriteFile(f, data, 0666) +} + +func findall(data []byte, prefix []byte, i int, minlz int) <-chan int { + c := make(chan int) + x0 := max(0, i-LZOFFSET) + x1 := min(i+minlz-1, len(data)) + f := 1 + go func() { + for f >= 0 { + f = bytes.LastIndex(data[x0:x1], prefix) + if f >= 0 { + c <- f + x0 + x1 = x0 + f + minlz - 1 + } + } + close(c) + }() + return c +} + +func findOptimalZeroRun(src []byte) int { + zeroruns := make(map[int]int) + var i = 0 + var j = 0 + for i < len(src)-1 { + if src[i] == 0 { + j = i + 1 + for j < len(src) && src[j] == 0 && j-i < 256 { + j += 1 + } + if j-i >= MINRLE { + zeroruns[j-i] = zeroruns[j-i] + 1 + } + i = j + } else { + i += 1 + } + } + if len(zeroruns) > 0 { + bestrun := 0 + bestvalue := 0.0 + for key, amount := range zeroruns { + currentvalue := float64(key) * math.Pow(float64(amount), 1.1) + if currentvalue > bestvalue { + bestrun = key + bestvalue = currentvalue + } + } + return bestrun + } else { + return LONGESTRLE + } +} + +func tokenCost(n0, n1 int, t byte) int64 { + size := int64(n1 - n0) + mdiv := int64(LONGESTLITERAL * (1 << 16)) + switch t { + case LZID: + return mdiv*2 + 134 - size + case LONGLZID: + return mdiv*3 + 134 - size + case RLEID: + return mdiv*2 + 128 - size + case ZERORUNID: + return mdiv * 1 + case LZ2ID: + return mdiv*1 + 132 - size + case LITERALID: + return mdiv*(size+1) + 130 - size + default: + os.Exit(-1) + } + return 0 +} + +func tokenPayload(src []byte, t token) []byte { + + n0 := t.i + n1 := t.i + t.size + + if t.tokentype == LZID { + return []byte{byte(LZMASK | (((t.size - 1) << 2) & 0x7f) | 2), byte(t.offset & 0xff)} + } else if t.tokentype == LONGLZID { + negoffset := (0 - t.offset) + return []byte{byte(LZMASK | (((t.size-1)>>1)<<2)&0x7f), byte(negoffset & 0xff), byte(((negoffset >> 8) & 0x7f) | (((t.size - 1) & 1) << 7))} + } else if t.tokentype == RLEID { + return []byte{RLEMASK | byte(((t.size-1)<<1)&0x7f), t.rlebyte} + } else if t.tokentype == ZERORUNID { + return []byte{RLEMASK} + } else if t.tokentype == LZ2ID { + return []byte{LZ2MASK | byte(0x7f-t.offset)} + } else { + return append([]byte{byte(LITERALMASK | t.size)}, src[n0:n1]...) + } +} + +func LZ(src []byte, i int, size int, offset int, minlz int) token { + var lz token + lz.tokentype = LZID + lz.i = i + if i >= 0 { + + bestpos := i - 1 + bestlen := 0 + + if len(src)-i >= minlz { + prefixes := findall(src, src[i:i+minlz], i, minlz) + for j := range prefixes { + l := minlz + for i+l < len(src) && l < LONGESTLONGLZ && src[j+l] == src[i+l] { + l++ + } + if l > bestlen { + bestpos = j + bestlen = l + } + } + } + lz.size = bestlen + lz.offset = i - bestpos + } else { + lz.size = size + lz.offset = offset + } + if lz.size > LONGESTLZ || lz.offset >= 256 { + lz.tokentype = LONGLZID + } + return lz +} + +func RLE(src []byte, i int, size int, rlebyte byte) token { + var rle token + rle.tokentype = RLEID + rle.i = i + if i >= 0 { + rle.rlebyte = src[i] + x := 0 + for i+x < len(src) && x < LONGESTRLE && src[i+x] == src[i] { + x++ + } + rle.size = x + } else { + rle.size = size + rle.rlebyte = rlebyte + } + return rle +} + +func ZERORUN(src []byte, i int, optimalRun int) token { + var zero token + zero.tokentype = ZERORUNID + + zero.i = i + zero.rlebyte = 0 + zero.size = 0 + + if i >= 0 { + var x int + for x = 0; x < optimalRun && i+x < len(src) && src[i+x] == 0; x++ { + } + if x == optimalRun { + zero.size = optimalRun + } + } + return zero +} + +func LZ2(src []byte, i int, size int, offset int) token { + var lz2 token + lz2.tokentype = LZ2ID + + lz2.offset = -1 + lz2.size = -1 + lz2.i = i + + if i >= 0 { + if i+2 < len(src) { + leftbound := max(0, i-LZ2OFFSET) + lpart := src[leftbound : i+1] + o := bytes.LastIndex(lpart, src[i:i+2]) + if o >= 0 { + lz2.offset = i - (o + leftbound) + lz2.size = 2 + } + } + } else { + lz2.size = size + lz2.offset = offset + } + return lz2 +} + +func LIT(i int, size int) token { + var lit token + lit.tokentype = LITERALID + lit.size = size + lit.i = i + return lit +} + +func crunchAtByte(src []byte, i int) { + rle := RLE(src, i, 0, 0) + //don't compute prefix for same bytes or this will explode + //start computing for prefixes larger than RLE + var lz token + if rle.size < LONGESTLONGLZ-1 { + lz = LZ(src, i, 0, 0, rle.size+1) + } else { + lz = LZ(src, -1, -1, -1, -1) // start with a dummy lz + } + + if lz.size >= MINLZ || rle.size >= MINRLE { + ms.Lock() + starts[i] = true + ms.Unlock() + } + + for size := lz.size; size >= MINLZ && size > rle.size; size-- { + me.Lock() + ends[i+size] = true + me.Unlock() + + mg.Lock() + graph[edge{i, i + size}] = LZ(src, -1, size, lz.offset, MINLZ) + mg.Unlock() + } + + for size := rle.size; size >= MINRLE; size-- { + me.Lock() + ends[i+size] = true + me.Unlock() + + mg.Lock() + graph[edge{i, i + size}] = RLE(src, -1, size, src[i]) + mg.Unlock() + } + + if len(src)-i > 2 { + lz2 := LZ2(src, i, 0, 0) + if lz2.size == 2 { + mg.Lock() + graph[edge{i, i + 2}] = lz2 //LZ2ID + mg.Unlock() + + ms.Lock() + starts[i] = true + ms.Unlock() + + me.Lock() + ends[i+2] = true + me.Unlock() + } + } + + zero := ZERORUN(src, i, optimalRun) + if zero.size != 0 { + mg.Lock() + graph[edge{i, i + optimalRun}] = zero + mg.Unlock() + + ms.Lock() + starts[i] = true + ms.Unlock() + + me.Lock() + ends[i+optimalRun] = true + me.Unlock() + } + + wg.Done() +} + +func crunch(src []byte, ctx crunchCtx) []byte { + + remainder := []byte{} + + var G = dijkstra.NewGraph() + + for i := 0; i < len(src)+1; i++ { + G.AddVertex(i) + } + + if ctx.INPLACE { + remainder = src[len(src)-1:] + src = src[:len(src)-1] + } + + optimalRun = findOptimalZeroRun(src) + + if !ctx.QUIET { + fmt.Println("Populating LZ layer") + } + + for i := 0; i < len(src); i++ { + wg.Add(1) + go crunchAtByte(src, i) + } + wg.Wait() + + starts[len(src)] = true + ends[0] = true + starts_ := make([]int, 0, len(starts)) + ends_ := make([]int, 0, len(ends)) + for k := range starts { + starts_ = append(starts_, k) + } + for k := range ends { + ends_ = append(ends_, k) + } + + sort.Ints(starts_) + sort.Ints(ends_) + + if !ctx.QUIET { + fmt.Println("Closing Gaps") + } + + e, s := 0, 0 + for e < len(ends_) && s < len(starts_) { + end := ends_[e] + if end < starts_[s] { + //bridge + for starts_[s]-end >= LONGESTLITERAL { + key := edge{end, end + LONGESTLITERAL} + _, haskey := graph[key] + if !haskey { + lit := LIT(end, LONGESTLITERAL) + lit.size = LONGESTLITERAL + graph[key] = lit + } + end += LONGESTLITERAL + } + s0 := s + for s0 < len(starts_) && starts_[s0]-end < LONGESTLITERAL { + key := edge{end, starts_[s0]} + _, haskey := graph[key] + if !haskey { + lit := LIT(end, starts_[s0]-end) + lit.size = starts_[s0] - end + graph[key] = lit + } + s0++ + } + e++ + } else { + s++ + } + } + + if !ctx.QUIET { + fmt.Println("Populating Graph") + } + + for k, t := range graph { + G.AddArc(k.n0, k.n1, tokenCost(k.n0, k.n1, t.tokentype)) + } + + if !ctx.QUIET { + fmt.Println("Computing shortest path") + } + + best, _ := G.Shortest(0, len(src)) + + crunched := make([]byte, 0) + token_list := make([]token, 0) + + for i := 0; i < len(best.Path)-1; i++ { + e := edge{best.Path[i], best.Path[i+1]} + token_list = append(token_list, graph[e]) + } + + if ctx.INPLACE { + safety := len(token_list) + segment_uncrunched_size := 0 + segment_crunched_size := 0 + total_uncrunched_size := 0 + for i := len(token_list) - 1; i >= 0; i-- { + segment_crunched_size += len(tokenPayload(src, token_list[i])) //token size + segment_uncrunched_size += token_list[i].size //decrunched token raw size + if segment_uncrunched_size <= segment_crunched_size+0 { + safety = i + total_uncrunched_size += segment_uncrunched_size + segment_uncrunched_size = 0 + segment_crunched_size = 0 + } + } + for _, t := range token_list[:safety] { + crunched = append(crunched, tokenPayload(src, t)...) + } + if total_uncrunched_size > 0 { + remainder = append(src[len(src)-total_uncrunched_size:], remainder...) + } + crunched = append(crunched, TERMINATOR) + crunched = append(crunched, remainder[1:]...) + crunched = append(remainder[:1], crunched...) + crunched = append([]byte{byte(optimalRun - 1)}, crunched...) + crunched = append(ctx.addr, crunched...) + + } else { + for _, t := range token_list { + crunched = append(crunched, tokenPayload(src, t)...) + } + crunched = append(crunched, TERMINATOR) + if !ctx.SFX { + crunched = append([]byte{byte(optimalRun - 1)}, crunched...) + } + } + + return crunched +} + +func main() { + var ctx crunchCtx + var jmp_str string + flag.BoolVar(&ctx.PRG, "p", false, "") + flag.BoolVar(&ctx.QUIET, "q", false, "") + flag.BoolVar(&ctx.INPLACE, "i", false, "") + flag.StringVar(&jmp_str, "x", "", "") + flag.Usage = usage + flag.Parse() + + if jmp_str != "" { + ctx.SFX = true + ctx.PRG = true + } + + if ctx.INPLACE { + ctx.PRG = true + } + + if flag.NArg() != 2 { + usage() + os.Exit(2) + } + + if ctx.SFX { + if jmp_str[0] == '$' { + jmp, err := strconv.ParseUint(jmp_str[1:], 16, 16) + if err == nil { + ctx.jmp = uint16(jmp) + } + } + if ctx.jmp == 0 { + usage() + os.Exit(2) + } + } + + ifidx := flag.NArg() - 2 + ofidx := flag.NArg() - 1 + + src := load_raw(flag.Args()[ifidx]) + + sourceLen := len(src) + + if ctx.PRG { + ctx.addr = src[:2] + src = src[2:] + + ctx.decrunchTo = uint16(ctx.addr[0]) + 256*uint16(ctx.addr[1]) + } + + crunched := crunch(src, ctx) + crunchedSize := len(crunched) + + if ctx.SFX { + fileLen := len(boot) + len(crunched) + startAddress := 0x10000 - len(crunched) + transfAddress := fileLen + 0x6ff + + boot[0x1e] = byte(transfAddress & 0xff) //transfer from + boot[0x1f] = byte(transfAddress >> 8) + + boot[0x3c] = byte(startAddress & 0xff) //Depack from.. + boot[0x3d] = byte(startAddress >> 8) + + boot[0x40] = byte(ctx.decrunchTo & 0xff) //decrunch to.. + boot[0x41] = byte(ctx.decrunchTo >> 8) + + boot[0x77] = byte(ctx.jmp & 0xff) // Jump to.. + boot[0x78] = byte(ctx.jmp >> 8) + + boot[0xc9] = byte(optimalRun - 1) + + crunched = append(boot, crunched...) + + crunchedSize += len(boot) + ctx.loadTo = 0x0801 + } + + decrunchEnd := uint16(int(ctx.decrunchTo) + len(src) - 1) + + if ctx.INPLACE { + ctx.loadTo = decrunchEnd - uint16(len(crunched)) + 1 + crunched = append([]byte{byte(ctx.loadTo & 255), byte(ctx.loadTo >> 8)}, crunched...) + } + + save_raw(flag.Args()[ofidx], crunched) + + if !ctx.QUIET { + ratio := (float32(crunchedSize) * 100.0 / float32(sourceLen)) + prg := "RAW" + dest_prg := "RAW" + if ctx.PRG { + prg = "PRG" + } + if ctx.SFX || ctx.INPLACE { + dest_prg = "prg" + } + fmt.Printf("input file %s: %s, $%04x - $%04x : %d bytes\n", + prg, flag.Args()[ifidx], ctx.decrunchTo, decrunchEnd, sourceLen) + fmt.Printf("output file %s: %s, $%04x - $%04x : %d bytes\n", + dest_prg, flag.Args()[ofidx], ctx.loadTo, crunchedSize+int(ctx.loadTo)-1, crunchedSize) + fmt.Printf("crunched to %.2f%% of original size\n", ratio) + } +} diff --git a/loader/tools/tscrunch/tscrunch.py b/loader/tools/tscrunch/tscrunch.py new file mode 100644 index 0000000..c29aa38 --- /dev/null +++ b/loader/tools/tscrunch/tscrunch.py @@ -0,0 +1,605 @@ +#!/usr/bin/env python + +""" +TSCrunch 1.3 - binary cruncher, by Antonio Savona +""" + +import sys + +REVERSELITERAL = False +VERBOSE = True +PRG = False +SFX = False +INPLACE = False + +DEBUG = False + +LONGESTRLE = 64 +LONGESTLONGLZ = 64 +LONGESTLZ = 32 +LONGESTLITERAL = 31 +MINRLE = 2 +MINLZ = 3 +LZOFFSET = 32767 +LZ2OFFSET = 94 + +RLEMASK = 0x81 +LZMASK = 0x80 +LITERALMASK = 0x00 +LZ2MASK = 0x00 + +TERMINATOR = LONGESTLITERAL + 1 + +ZERORUNID = 4 +LZ2ID = 3 +LZID = 2 +RLEID = 1 +LITERALID = 0 + + +from scipy.sparse.csgraph import dijkstra +from scipy.sparse import csr_matrix + +boot = [ + + 0x01, 0x08, 0x0B, 0x08, 0x0A, 0x00, 0x9E, 0x32, 0x30, 0x36, 0x31, 0x00, + 0x00, 0x00, 0x78, 0xA2, 0xC9, 0xBD, 0x1A, 0x08, 0x95, 0x00, 0xCA, 0xD0, + 0xF8, 0x4C, 0x02, 0x00, 0x34, 0xBD, 0x00, 0x10, 0x9D, 0x00, 0xFF, 0xE8, + 0xD0, 0xF7, 0xC6, 0x04, 0xC6, 0x07, 0xA5, 0x04, 0xC9, 0x07, 0xB0, 0xED, + 0xA0, 0x00, 0xB3, 0x21, 0x30, 0x21, 0xC9, 0x20, 0xB0, 0x3F, 0xA8, 0xB9, + 0xFF, 0xFF, 0x88, 0x99, 0xFF, 0xFF, 0xD0, 0xF7, 0x8A, 0xE8, 0x65, 0x25, + 0x85, 0x25, 0xB0, 0x77, 0x8A, 0x65, 0x21, 0x85, 0x21, 0x90, 0xDF, 0xE6, + 0x22, 0xB0, 0xDB, 0x4B, 0x7F, 0x90, 0x3A, 0xF0, 0x6B, 0xA2, 0x02, 0x85, + 0x53, 0xC8, 0xB1, 0x21, 0xA4, 0x53, 0x91, 0x25, 0x88, 0x91, 0x25, 0xD0, + 0xFB, 0xA9, 0x00, 0xB0, 0xD5, 0xA9, 0x37, 0x85, 0x01, 0x58, 0x4C, 0x5B, + 0x00, 0xF0, 0xF6, 0x09, 0x80, 0x65, 0x25, 0x85, 0x9B, 0xA5, 0x26, 0xE9, + 0x00, 0x85, 0x9C, 0xB1, 0x9B, 0x91, 0x25, 0xC8, 0xB1, 0x9B, 0x91, 0x25, + 0x98, 0xAA, 0x88, 0xF0, 0xB1, 0x4A, 0x85, 0xA0, 0xC8, 0xA5, 0x25, 0x90, + 0x33, 0xF1, 0x21, 0x85, 0x9B, 0xA5, 0x26, 0xE9, 0x00, 0x85, 0x9C, 0xA2, + 0x02, 0xA0, 0x00, 0xB1, 0x9B, 0x91, 0x25, 0xC8, 0xB1, 0x9B, 0x91, 0x25, + 0xC8, 0xB9, 0x9B, 0x00, 0x91, 0x25, 0xC0, 0x00, 0xD0, 0xF6, 0x98, 0xA0, + 0x00, 0xB0, 0x83, 0xE6, 0x26, 0x18, 0x90, 0x84, 0xA0, 0xFF, 0x84, 0x53, + 0xA2, 0x01, 0xD0, 0x96, 0x71, 0x21, 0x85, 0x9B, 0xC8, 0xB3, 0x21, 0x09, + 0x80, 0x65, 0x26, 0x85, 0x9C, 0xE0, 0x80, 0x26, 0xA0, 0xA2, 0x03, 0xD0, + 0xC4 + + ] + +def load_raw(fi): + data = bytes(fi.read()) + return data + +def save_raw(fo, data): + fo.write(bytes(data)) + +#finds all the occurrences of prefix in the range [max(0,i - LZOFFSET),i) +#the search window is quite small, so brute force here performs as well as suffix trees +def findall(data,prefix,i,minlz = MINLZ): + x0 = max(0,i - LZOFFSET) + x1 = min(i + minlz - 1, len(data)) + f = 1 + while f >= 0: + f = data.rfind(prefix,x0,x1) + if f >= 0: + yield f + x1 = f + minlz - 1 + +#pretty prints a progress bar +def progress(description,current,total): + percentage = 100 *current // total + tchars = 16 * current // total + sys.stdout.write("\r%s [%s%s]%02d%%" %(description,'*'*tchars, ' '*(16-tchars), percentage)) + + +def findOptimalZero(src): + zeroruns = dict() + i = 0 + while i < len(src) - 1: + + if src[i] == 0: + j = i + 1 + while j < len(src) and src[j] == 0 and j-i < 256: + j+=1 + if j - i >= MINRLE: + zeroruns[j-i] = zeroruns.get(j-i,0) + 1 + i = j + else: + i+=1 + + if len(zeroruns) > 0: + return min(list(zeroruns.items()),key = lambda x:-x[0]*(x[1]**1.1))[0] + else: + return LONGESTRLE + +class Token: + def __init__(self,src = None): + self.type = None + + +class ZERORUN(Token): + def __init__(self,src,i,size = LONGESTRLE, token = None): + self.type = ZERORUNID + self.size = size + if token != None: + self.fromToken(token) + else: + if not(i+size < len(src) and src[i:i+size] == bytes([0] * size)): + self.size = 0 + + def getCost(self): + return 1 + + def getPayload(self): + return [RLEMASK] + +class RLE(Token): + def __init__(self,src,i,size = None, token = None): + self.type = RLEID + self.rleByte = src[i] + + if token != None: + self.fromToken(token) + + elif size == None: + x = 0 + while i + x < len(src) and x < LONGESTRLE and src[i + x] == src[i]: + x+=1 + self.size = x + else: + self.size = size + + def getCost(self): + return 2 + 0.00128 - 0.00001 * self.size + + def getPayload(self): + return [RLEMASK | (((self.size-1) << 1) & 0x7f ), self.rleByte] + + +class LZ(Token): + def __init__(self,src,i, size = None, offset = None, minlz = MINLZ, token = None): + self.type = LZID + + if token != None: + self.fromToken(token) + + elif size == None: + + bestpos , bestlen = i - 1 , 0 + + if len(src) - i >= minlz: + for j in findall(src,src[i:i+minlz],i,minlz): + + l = minlz + while i + l < len(src) and l < LONGESTLONGLZ and src[j + l] == src[i + l] : + l+=1 + if l > bestlen: + bestpos, bestlen = j , l + + self.size = bestlen + self.offset = i - bestpos + + else: + self.size = size + if offset != None: + self.offset = offset + + def getCost(self): + return (2 if (self.offset < 256) and (self.size <= LONGESTLZ) else 3) + 0.00134 - 0.00001 * self.size + + def getPayload(self): + if self.offset >= 256 or self.size > LONGESTLZ: + negoffset = (0-self.offset) + return [LZMASK | ((((self.size - 1)>>1)<< 2) & 0x7f) | 0 , (negoffset & 0xff) , ((negoffset >> 8) & 0x7f) | (((self.size - 1) & 1) << 7 )] + else: + return [LZMASK | (((self.size - 1)<< 2) & 0x7f) | 2 , (self.offset & 0xff) ] + + +class LZ2(Token): + def __init__(self,src,i, offset = None, token = None): + self.type = LZ2ID + self.size = 2 + + if token != None: + self.fromToken(token) + + elif offset == None: + if i+2 < len(src): + o = src.rfind(src[i:i+2], max(0,i-LZ2OFFSET),i + 1) + if o >= 0: + self.offset = i - o + else: + self.offset = -1 + + else: + self.offset = - 1 + + else: + self.offset = offset + + + def getCost(self): + return 1 + 0.00132 - 0.00001 * self.size + + def getPayload(self): + return [LZ2MASK | (127 - self.offset) ] + + +class LIT(Token): + def __init__(self,src,i, token = None): + self.type = LITERALID + self.size = 1 + self.start = i + + if token != None: + self.fromToken(token) + + def getCost(self): + return self.size + 1 + 0.00130 - 0.00001 * self.size + + def getPayload(self): + return bytes([LITERALMASK | (self.size)]) + src[self.start : self.start + self.size] + + +class Cruncher: + + def __init__(self, src = None): + self.crunched = [] + self.token_list = [] + self.src = src + self.graph = dict() + self.crunchedSize = 0 + + def get_path(self,p): + i = len(p) - 1 + path = [i] + while p[i] >= 0: + path.append(p[i]) + i = p[i] + path.reverse() + + return list(zip(path[::],path[1::])) + + def prepend(self, data): + self.crunched = bytes(data) + bytes(self.crunched) + + def ocrunch(self): + starts = set() + ends = set() + + if INPLACE: + remainder = self.src[-1:] + src = bytes(self.src[:-1]) + else: + src = bytes(self.src) + + + self.optimalRun = findOptimalZero(src) + + progress_string = "Populating LZ layer\t" + + for i in range(0,len(src)): + if VERBOSE and ((i & 255) == 0): + progress(progress_string,i,len(src)) + lz2 = None + rle = RLE(src,i) + + #don't compute prefix for same bytes or this will explode + #start computing for prefixes larger than RLE + if rle.size < LONGESTLONGLZ - 1: + lz = LZ(src,i, minlz = rle.size + 1) + else: + lz = LZ(src,i,size = 1) #start with a dummy LZ + + if lz.size >= MINLZ or rle.size >= MINRLE: + starts.add(i) + while lz.size >= MINLZ and lz.size > rle.size: + ends.add(i+lz.size) + self.graph[(i,i+lz.size)] = lz + lz = LZ(src, i, size = lz.size - 1, offset = lz.offset) + while rle.size >= MINRLE: + ends.add(i+rle.size) + self.graph[(i,i+rle.size)] = rle + rle = RLE(src, i, rle.size - 1) + + lz2 = LZ2(src,i) + if lz2.offset > 0: + self.graph[(i,i+2)] = lz2 + starts.add(i) + ends.add(i + 2) + + zero = ZERORUN(src,i,self.optimalRun) + if zero.size > 0: + self.graph[(i,i+self.optimalRun)] = zero + starts.add(i) + ends.add(i+self.optimalRun) + + + if VERBOSE: + progress(progress_string,1,1) + sys.stdout.write('\n') + + starts.add(len(src)) + starts = sorted(list(starts)) + ends = [0] + sorted(list(ends)) + + progress_string = "Closing gaps\t\t" + + e,s = 0,0 + while e < len(ends) and s < len(starts): + if VERBOSE and ((s & 255) == 0): + progress(progress_string,s,len(starts)) + end = ends[e] + if end < starts[s]: + #bridge + while starts[s] - end >= LONGESTLITERAL: + key = (end,end + LONGESTLITERAL) + if not key in self.graph: + lit = LIT(src,end) + lit.size = LONGESTLITERAL + self.graph[key] = lit + end+=LONGESTLITERAL + s0 = s + while s0 < len(starts) and starts[s0] - end < LONGESTLITERAL: + key = (end,starts[s0]) + if not key in self.graph: + lit = LIT(src,end) + lit.size = starts[s0] - end + self.graph[key] = lit + s0+=1 + e+=1 + else: + s+=1 + + if VERBOSE: + progress(progress_string,1,1) + sys.stdout.write('\n') + + progress_string = "Populating graph\t" + + if VERBOSE: + progress(progress_string,0,3) + weights = tuple(v.getCost() for v in self.graph.values()) + if VERBOSE: + progress(progress_string,1,3) + sources = tuple(s for s, _ in self.graph.keys()) + if VERBOSE: + progress(progress_string,2,3) + targets = tuple(t for _, t in self.graph.keys()) + n = len(src) + 1 + dgraph = csr_matrix((weights, (sources, targets)), shape=(n, n)) + if VERBOSE: + progress(progress_string,1,1) + sys.stdout.write('\ncomputing shortest path\n') + d,p = dijkstra(dgraph,indices = 0,return_predecessors = True) + for key in self.get_path(p): + self.token_list.append(self.graph[key]) + + if INPLACE: + safety = len(self.token_list) + segment_uncrunched_size = 0 + segment_crunched_size = 0 + total_uncrunched_size = 0 + for i in range(len(self.token_list)-1,-1,-1): + segment_crunched_size+=len(self.token_list[i].getPayload()) #token size + segment_uncrunched_size+=self.token_list[i].size #decrunched token raw size + if segment_uncrunched_size <= segment_crunched_size + 0: + safety = i + total_uncrunched_size+=segment_uncrunched_size + segment_uncrunched_size = 0 + segment_crunched_size = 0 + + for token in (self.token_list[:safety]): + self.crunched.extend(token.getPayload()) + if total_uncrunched_size > 0: + remainder = src[-total_uncrunched_size:] + remainder + self.crunched.extend(bytes([TERMINATOR]) + remainder[1:]) + self.crunched = addr + bytes([self.optimalRun - 1]) + remainder[:1] + bytes(self.crunched) + + else: + if not SFX: + self.crunched.extend([self.optimalRun - 1]) + for token in (self.token_list): + self.crunched.extend(token.getPayload()) + self.crunched = bytes(self.crunched + [TERMINATOR]) + self.crunchedSize = len(self.crunched) + + if DEBUG: + nlz2 = 0; nlzl = 0; nlz = 0; nrle = 0; nlit = 0; nz = 0; nlit1 = 0 + + for token in self.token_list: + if token.type == LITERALID: + nlit+=1 + if token.size == 1: + nlit1+=1 + elif token.type == LZ2ID: + nlz2+=1 + elif token.type == RLEID: + nrle +=1 + elif token.type == ZERORUNID: + nz +=1 + else: + if len(token.getPayload()) == 3: + nlzl+=1 + else: + nlz+=1 + + tot = sum((nlz,nlzl,nlz2,nrle,nz,nlit)) + sys.stdout.write ("lz: %d, lzl: %d, lz2: %d, rle: %d, nz: %d, lit: %d (1 = %d) tot: %d\n" % (nlz,nlzl,nlz2,nrle,nz,nlit,nlit1,tot)) + + +class Decruncher: + def __init__(self, src = None): + + self.src = src + self.decrunch() + + def decrunch(self, src = None): + + if src != None: + self.src = src + if self.src == None: + self.decrunched = None + else: + + nlz2 = 0; nlz = 0; nrle = 0; nz = 0; nlit = 0; + + self.decrunched = bytearray([]) + self.optimalRun = self.src[0] + 1 + i=1 + while self.src[i] != TERMINATOR: + + code = self.src[i] + if ((code & 0x80 == LITERALMASK) and code & 0x7f < 32) : + + run = (code & 0x1f) + chunk = self.src[i + 1 : i + run + 1] + if REVERSELITERAL: + chunk.reverse() + self.decrunched.extend(chunk) + i+=run + 1 + nlit+=1 + + elif (code & 0x80 == LZ2MASK): + + run = 2 + offset = 127 - (code & 0x7f) + p = len(self.decrunched) + for l in range(run): + self.decrunched.append(self.decrunched[p-offset + l]) + i+=1 + nlz2+=1 + + elif (code & 0x81) == RLEMASK and (code & 0x7e) != 0: + run = ((code & 0x7f) >> 1) + 1 + self.decrunched.extend([self.src[i+1]] * run) + i+=2 + nrle+=1 + + elif (code & 0x81) == RLEMASK and (code & 0x7e) == 0: + run = self.optimalRun + self.decrunched.extend(bytes([0] * run)) + i+=1 + nz+=1 + + else: + if (code & 2) == 2: + run = ((code & 0x7f) >> 2) + 1 + offset = self.src[i+1] + i+=2 + else: + lookahead = self.src[i+2] + run = 1 + (((code & 0x7f) >> 2) << 1) + (1 if (lookahead & 128 == 128) else 0) + offset = 32768 - (self.src[i+1] + 256 * (lookahead & 0x7f)) + i+=3 + p = len(self.decrunched) + for l in range(run): + self.decrunched.append(self.decrunched[p-offset + l]) + nlz+=1 + + tot = sum((nlz,nlz2,nrle,nz,nlit)) + sys.stdout.write ("lz: %d, lz2: %d, rle: %d, nz: %d, lit: %d tot: %d\n" % (nlz,nlz2,nrle,nz,nlit,tot)) + +def usage(): + print ("TSCrunch 1.3 - binary cruncher, by Antonio Savona") + print ("Usage: tscrunch [-p] [-i] [-r] [-q] [-x] infile outfile") + print (" -p : input file is a prg, first 2 bytes are discarded") + print (" -x $addr: creates a self extracting file (forces -p)") + print (" -i : inplace crunching (forces -p)") + print (" -q : quiet mode") + + +if __name__ == "__main__": + + if "-h" in sys.argv or len(sys.argv) < 3: + usage() + else: + + if "-q" in sys.argv: + VERBOSE = False + + if "-x" in sys.argv: + SFX = True + PRG = True + jmp_str = sys.argv[sys.argv.index("-x") + 1].strip("$") + jmp = int(jmp_str,base = 16) + + if "-i" in sys.argv: + INPLACE = True + PRG = True + + if "-p" in sys.argv: + PRG = True + + if SFX and INPLACE: + sys.stderr.write ("Can't create an sfx prg with inplace crunching\n") + exit(-1) + + fr = open(sys.argv[-2],"rb") + src = load_raw(fr) + + sourceLen = len(src) + + decrunchTo = 0 + loadTo = 0 + + if PRG: + addr = src[:2] + src = src[2:] + decrunchTo = addr[0] + 256 * addr[1] + + cruncher = Cruncher(src) + cruncher.ocrunch() + + if SFX: + + fileLen = len(boot) + len(cruncher.crunched) + startAddress = 0x10000 - len(cruncher.crunched) + transfAddress = fileLen + 0x6ff + + boot[0x1e] = transfAddress & 0xff #transfer from + boot[0x1f] = transfAddress >> 8 + + boot[0x3c] = startAddress & 0xff # Depack from.. + boot[0x3d] = startAddress >> 8 + + boot[0x40] = decrunchTo & 0xff # decrunch to.. + boot[0x41] = decrunchTo >> 8 + + boot[0x77] = jmp & 0xff; # Jump to.. + boot[0x78] = jmp >> 8; + + boot[0xc9] = cruncher.optimalRun - 1 + + cruncher.prepend(boot) + + cruncher.crunchedSize+=len(boot) + loadTo = 0x0801 + + decrunchEnd = decrunchTo + len(src) - 1 + + if INPLACE: + loadTo = decrunchEnd - len(cruncher.crunched) + 1 + cruncher.prepend([loadTo & 255, loadTo >> 8]) + + fo = open(sys.argv[-1],"wb") + + save_raw(fo,cruncher.crunched) + fo.close() + + if VERBOSE: + ratio = (float(cruncher.crunchedSize) * 100.0 / sourceLen) + print ("input file %s: %s, $%04x - $%04x : %d bytes" + %("PRG" if PRG else "RAW", sys.argv[-2], decrunchTo, decrunchEnd, sourceLen)) + print ("output file %s: %s, $%04x - $%04x : %d bytes" + %("PRG" if SFX or INPLACE else "RAW", sys.argv[-1], loadTo, cruncher.crunchedSize + loadTo - 1, cruncher.crunchedSize)) + print ("crunched to %.2f%% of original size" %ratio) + + if DEBUG and not (SFX or INPLACE): + decruncher = Decruncher(cruncher.crunched) + + fo = open("test.raw","wb") + + save_raw(fo,decruncher.decrunched) + fo.close() + + assert(decruncher.decrunched == src) diff --git a/loader/tools/wcrush/LICENSE.TXT b/loader/tools/wcrush/LICENSE.TXT new file mode 100644 index 0000000..e19731c --- /dev/null +++ b/loader/tools/wcrush/LICENSE.TXT @@ -0,0 +1,8 @@ +Terms of Use: + +0. You may do whatever you want with this stuff, as long as you allow others to do what they want with it, too. + +1. NO WARRANTY, USE AT YOUR OWN RISK! + +That's it. + diff --git a/loader/tools/wcrush/Makefile b/loader/tools/wcrush/Makefile new file mode 100644 index 0000000..ff189e3 --- /dev/null +++ b/loader/tools/wcrush/Makefile @@ -0,0 +1,12 @@ + +all: wca/wca wcrush/wcrush + +wca/wca: wca/main.cpp + gcc -O3 -o wca/wca wca/main.cpp + +wcrush/wcrush: wcrush/main.cpp + gcc -O3 -o wcrush/wcrush wcrush/main.cpp + +clean: + rm -rf wca/wca + rm -rf wcrush/wcrush \ No newline at end of file diff --git a/loader/tools/wcrush/README.TXT b/loader/tools/wcrush/README.TXT new file mode 100644 index 0000000..4ca5842 --- /dev/null +++ b/loader/tools/wcrush/README.TXT @@ -0,0 +1,49 @@ +WHAT THIS IS ALL ABOUT: + +Crush for Windows was developed as a replacement for Taboo's crush.exe levelcrusher, which unfortunately doesn't work on 64 bit Windows without ugly tricks like using DosBox etc. +The binaries included work on Windows 98SE and Windows 8.1 64 bit, so they should (maybe) work on all Windows versions that came in between (hopefully). + + +WHY EVEN USE THIS: + +No idea, really, as ALZ and Exomizer pack much better, and even Pucrunch beats it. But crush is quite fast and the depacker is easy to understand and thus easy to customize, and can be made pretty tiny, too. Apart from that you might want to use it just for nostalgic reasons if you are one of those guys. + + +WARNING: + +This early vesrion may have lots of bugs and could make your PC catch fire and/or explode, use at your own risk! + +This is also the first PC program I wrote after a 20 hiatus, and the first somewhat complex C(++) program I ever wrote, too, googling every second instruction in the process, so the source code will likely make seasoned C-programmers cringe - read it at your own risk, too! + +The working principles of the packer were derived from analysing the C64-end depacker and my implementation of the resulting compression algorithm is pretty naive and inefficient, making wcrush up to 6 or 7 times slower than the original. On a 1300MHz Celeron the worst case still took just ca. 0.3 seconds to pack though, so on more modern PCs the speed-difference might not be that notable. + + +USAGE: + +wcrush [output file] + +where must range from 0 (the worst compression) to 6 (the best), and must be a binary that is neither shorter than 4 bytes nor longer than 65535 bytes. If no output filename is specified the output file will be named "crushed". + + +INCLUDED IN THIS RELEASE: + +[wcrush]: +Windows binary and source code + +[decrush] +a slightly modified and heavily commented version of the original C64-end depacker by Taboo, that is a bit slower, but fits in the stack up to ca. $1d8 for depacking of extra large files. Does not handle $01, $d030 etc. after depacking, do that in the payload or use the original Taboo depacker instead. Assemble with 64Tass/6502Tass 1.31 or later. + +[wca] +Windows binary and source code of the crushfile analyzer, a tool that prints out how the depacker on the C64 would handle a crushed binary. Numbers in brackets (eg. "(0080:2f)") are the last address(es) and value(s) of the control bytes read between perfoming the previous and the current action. The sole purpose of this hackjob was to compare the outputs of original crush and wcrush to aid my debugging efforts, it is just included for fun. + + +ADDITIONAL CREDITS AND THANKS: + +Taboo for the original crush & decrush.tas +Groepaz/Hitman for the makefile +stackoverflow.com and cplusplus.com for without those sites I would have been completely lost. + + +That's all, thanks for reading and have a nice day, + +CS. diff --git a/loader/tools/wcrush/decrush/decrush.tas b/loader/tools/wcrush/decrush/decrush.tas new file mode 100644 index 0000000..4bc846b --- /dev/null +++ b/loader/tools/wcrush/decrush/decrush.tas @@ -0,0 +1,321 @@ +;Level-crusher depacker +;v1.0 (c)1998 Taboo Productions! +;All rights reserved + +;v1.1 by CS, slightly modified to fit into stack, max outputsize ca. $0200-$fffc + + +speed = 6 ;speed with which the file was crushed (=compression rate), + ;always use 6 for max compression unless you still use an 8088 +sysline = 1 ;1=add Basic SYS line, 0=plain binary file +linenum = 2001 ;place Basic line number here (ignored if sysline=0) +exeaddr = 2070 ;of the payload, of course +debug = 0 ;1=indicate progress via bordercolors + ;0=no progress indication (=faster depacking & shorter depacker) + + +;no longer used, all that stuff has to be handled by the payload now: + +val01 = $37 ;value of $01 system register after depack + ;C64 power-on default = $37, but other values might be needed, + ;depending on what the payload expects. +cliflag = 0 ;1=reenable, 0=keep disabled interrupts after depack + ;(IRQ is most of the time reenabled by the payload, + ;but sometimes you have to do that manually) +blank = 0 ;1= leave screen blank, 0=enable after depack (dito) + + +*=$801 + +.if sysline + .word eolptr ;start address of next(=last) basic line + .word linenum ;line number of sysline (as set above) + .byte $9e ;basic token for SYS +sysnum .byte 0,0,0,0 ;placeholders for SYS address (see below) + .text "" ;put your crazy SYSline talk here if required + .byte 0 ;end of basic line +eolptr .byte 0,0 ;next basic line, 2x0 = end of basic text +.fi + +start sei + +.if sysline +temp=* +*=sysnum + .byte ^start ;replace placeholders with correct petscii-digits (see above) +*=temp +.fi + + ldx #0 ;black border + stx $d020 + stx $d011 ;screen off + lda #$1 ;set c128 to 2MHz mode + sta $d030 + lda #$34 ;all ram memconfig. + sta $1 + +movecode lda depacker,x ;copy depacker+some crap to $100 - $1ff + txs ;init stack ptr while we're at it, clever, eh? + pha + inx + bne movecode + +;move input stream to memtop, .Y=# of pages to copy: + + ldy #>(endofprog-crushed+$ff) +movedata lda endofprog-$100,x + sta $ff00,x ;starting at the last page + inx + bne movedata + dec movedata+2 ;previous page src + dec movedata+5 ;previous page tgt + dey + bne movedata ;repeat until all pages copied + +;set source = start of moved input stream: + + lda #<$10000-endofprog+crushed + sta @b src + lda #>$10000-endofprog+crushed + sta @b srch + +;set destination = load address of packed file: + + lda depackaddr + sta @b dest + lda depackaddr+1 + sta @b desth + jmp godepack ;start unpacking + +;------------------------------------------- +;zeropage registers used by decompressor: + +src = $ae ;current input-file address ptr. +srch = src+1 + +dest = $2d ;depack-to address pointer +desth = dest+1 + +byte = $fb ;control bit dispense byte +lng = $fc ;sequence length +lo = $fd ;for copy address +hi = lo+1 ;and various 16 bit stuff +;-------------------------------------------------------- +;the depack algorythm, living in the stack up to ca. $1d7 + +depacker +.logical $100 + +godepack ldx #$00 ;empty the control bit dispenser + stx byte ;to force reading it from input-file right away + +nextaction stx hi ;DEPACK MAINLOOP: hi always = 0 here + lda #$01 ;length of upcoming run is at least 1 byte + jsr getbit ;get sequence flag: + bcs repeat ;if set then repeat a previous byte sequence + +;else copy a run of uncompressed bytes. +;the length of that run is stored as stopbit,bit,... sequence: + +getcopylen jsr getbit ;get length of uncompressed run: + bcs gotcopylen ;if stopbit set then end of run + + jsr getbit ;else shift length bit(s) + + rol ;into accu, hi + rol hi + bpl getcopylen ;max length is $ffff + +gotcopylen tax ;got length, + beq copypage ;if it is a multiple of 256 then... + +copybytes ldy #$00 ;(else) copy .X src-bytes + lda (src),y + inc src + bne cb1 + inc srch +cb1 sta (dest),y ;to output stream + inc dest + bne cb2 + inc desth +cb2 dex + bne copybytes + +copypage cpx hi ;...check pages left to copy + dec hi + bcc copybytes ;if pages left then copy 256 more src-bytes + stx hi ;else hi=0, done. + +;having just copied an uncompressed sequence we can safely assume the next sequence +;matches something already in the output data and thus don't need any +;control bits to tell the next step: + +;repeat an earlier byte sequence at the current output position. +;first, get sequence length which is encoded as a stopbit, bit, ... sequence again, +;but is preceeded by a special length-flag: + +repeat lda #$01 ;repeat a sequence: dft length=1 + jsr getbit ;get length-flag: + bcc rep2bytes ;if = 0 then the sequence length 2-bytes + +getreplen jsr getbit ;else 3+ bytes, get next stop bit + bcs gotreplen ;if set then end of length bits + + jsr getbit ;else get next length bit + rol + bcc getreplen ;repeat if length still < 256 + +;else a length of 256+ bytes signals that we have reached end of input file: + + jmp exeaddr ;we're done with depacking + +rep2bytes inx ;X=1 to use special 2-byte address-chunks + +;C=0 only for 2 byte sequences here, for all other sequences, C=1: + +gotreplen adc #$01 ;true length = length+C+1 + sta lng + +;now the offset to the sequence to copy is computed. this is quite complicated :-) +;the number of "bit-chunks" forming the offset is retrieved from 2 more control bits. +;up to 4 chunks can be chained together, containing 2-4 bits each, depending on pack speed +;and sequence length, where 2-byte sequences use a seperate chunk table with an overall +;shorter search range to ensure that less than 16 bits are needed to encode the entire +;sequence specs: + + txa ;A=1 for 2byters here, else 0 + jsr getbit ;get number of bit-chunks needed for offset + rol + jsr getbit + rol + tay ;Y now = 0-3 for 3+byters or 4-7 for 2byters + lda #$00 + +;after that, look up how many bits are in the current chunk and read in as many offset bits. +;if one more chunks follow, add 1 to the current offset and repeat with next chunk: +;the maximum offset range that can be achieved by this method is +;2^[bits of all used chunks]+2^[bits of all but last chunk]+...+2^[bits of last chunk]-1 + +getnumbits ldx tab,y ;get number of bits used in current chunk + +getoffset jsr getbit ;MSB comes first + rol ;shift offset bits to A, hi + rol hi + dex ;next bit of current chunk + bne getoffset + + dey ;next chunk or copy done?: + bmi gotoffset+1 ;if last 3+ byte chunk already processed then... + cpy #$03 + beq gotoffset ;or if last 2 byte chunk already processed then... + + clc ;else another chunk will follow, + adc #$01 ;extend offset range by 2^(bits still to follow) + bcc getnumbits ;to save some more bits + inc hi + bcs getnumbits + +;after all required offset bits have been retrieved, calculate the start address of the +;sequence to repeat and append the specified amount of bytes from there at the current +;output position. The offset stored in the control bits points at the end of that +;sequence, so sequence length is added to find out the start address. This saves some +;bits but also makes it impossible to have repetitions overlap the current output +;position what could improve the pack rate in some situations. + +gotoffset clc ;...(required for 2 byte sequences only) + adc lng ;...offset = offset + sequence length + bcc calcaddr + inc hi + +calcaddr clc ;offset *-1 + current output address + sbc dest + eor #$ff + sta lo ;= source address of sequence to copy + lda hi + sbc desth + eor #$ff + sta hi + ldy #$00 + +repeatbytes lda (lo),y ;append bytes from at output pos + sta (dest),y + iny + cpy lng + bne repeatbytes + + tya ;new output pos = old pos. + + clc + adc dest + sta dest + bcc rb2 + inc desth + +rb2 ldx #$00 ;back to depack loop start + jmp nextaction + +;control bit dispenser readout. In the original depacker the bit readout was +;inlined whereever bits were needed. Now included in this subroutine to make the +;depacker a few bytes shorter (but also several cycles slower, unfortunately): + +getbit asl byte ;get a control bit: next bit -> C + bne gotbit ;if still bits left then done + + pha ;else get next control byte: save .A, .Y + sty byte + ldy #$00 + lda (src),y ;read from current input stream position + inc src + bne gb2 + inc srch + +gb2 ldy byte ;restore .Y + +.if debug + inc $01 ;some color effects to show progress + sta $d020 ;useful to see if/where depacking stalls + dec $01 +.fi + sec ;"out of bits" marker + rol ;1st control bit to C, marker to dispenser-byte + sta byte + pla + +gotbit rts + + +;crush-speed dependant offset chunk width tables: +;lower speed settings shorten the search range for duplicate sequences, thereby +;worsening the compression rate but also speeding up depacking a little and packing +;even more so (not that you'd notice that anymore on a half decent PC, though). +;it might also speed up depacking because there might be more uncompressed bytes +;which are very straightforward to depack. +;the last 4 values are used for 2 byte sequences, the first 4 for any other size. + +.if speed=6 +tab .byte 4,3,3,3, 4,2,2,2 +.fi +.if speed=5 +tab .byte 4,2,3,3, 4,2,2,2 +.fi +.if speed=4 +tab .byte 4,2,2,3, 4,2,2,2 +.fi +.if speed=3 +tab .byte 4,2,2,2, 4,2,2,2 +.fi +.if speed=2 +tab .byte 3,2,2,2, 3,2,2,2 +.fi +.if speed=1 +tab .byte 3,1,2,2, 3,1,2,2 +.fi +.if speed=0 +tab .byte 2,2,1,1, 2,2,1,1 +.fi + +.here + +depackaddr ;the first two bytes of the crushed file are the load address +crushed =*+2 ;crunched data starts right behind +.binary crushed.bin +endofprog =* diff --git a/loader/tools/wcrush/wca/main.cpp b/loader/tools/wcrush/wca/main.cpp new file mode 100644 index 0000000..a444a0c --- /dev/null +++ b/loader/tools/wcrush/wca/main.cpp @@ -0,0 +1,209 @@ +// this pretty much does the same as the taboo depacker on the C64-end +// (aka decrush.tas), except it doesn't run on a C64 but a PC and it's +// sole purpose is to walk through a raw crushed binary and tell you +// how the depacker would interprete the information stored in it. +// or in other words, it's just a debugging aid for the levelcrusher I wrote. + +// latest changes: added some sanity checks to keep operations within buffer limits (hopefully) + +// Note: does currently only support files crushed with max compression ("speed"=6) + +// #include //VS 2013 stuff, remove or change to whatever your dev-tools require +#include +#include +#include + +#define maxsize 0xffff + +const uint8_t packbits[56] = { + 2, 2, 1, 1, 2, 2, 1, 1, // worst compression (speed 0) + 3, 1, 2, 2, 3, 1, 2, 2, + 3, 2, 2, 2, 3, 2, 2, 2, + 4, 2, 2, 2, 4, 2, 2, 2, + 4, 2, 2, 3, 4, 2, 2, 2, + 4, 2, 3, 3, 4, 2, 2, 2, + 4, 3, 3, 3, 4, 2, 2, 2 }; // best compression (speed 6) + +uint8_t inbuf[maxsize+1]; +uint8_t outbuf[maxsize+1]; // dummy output, only there to be able to show repeat byte sequences + +uint32_t srcidx, tgtidx, refidx, offset, ctrlbyte; +int32_t chunkofs, len, chunks, chunkwidth, bitnum, columns; +uint64_t fsize; // just in case someone stupid (aka me) tries to run a TB-sized file through the analyzer +int speed = 6; + +// show the data bytes currently handled by depacker in somewhat formatted fashion: + +void movedata(uint8_t buffer1[], uint8_t buffer2[], uint32_t& idx1, uint32_t& idx2, int32_t len) +{ + columns = 16; + printf("%04x: ", idx2); + while (0 < len--) // to filter out negative lens as well, just in case... + { + printf(" %02x", buffer1[idx1]); + buffer2[idx2++] = buffer1[idx1++]; + if (--columns == 8) + printf(" "); + if (!columns) + { + printf("\n"); + if (len) + printf("%04x: ", idx2); + columns = 16; + } + } + printf("\n"); + if (columns != 16) + printf("\n"); +} + + +// extract control bit from input buffer, return 1 if set, else 0. +// also show value [and address] of current control byte + +uint16_t getbit() +{ + if (--bitnum <0) + { + ctrlbyte = inbuf[srcidx]; + bitnum = 7; + printf("(%04X:%02X)", srcidx,ctrlbyte); + if (srcidx < fsize) // sanity check + srcidx++; + } + return (ctrlbyte &(1<>bitnum; +} + + + +int main(int argc, char* argv[]) +{ + printf("\ncrushfile analyzer v0.1b by manic mailman\n\n"); + +// some help for the end user if input parameter is missing: + + if (argc < 2) + { + printf("usage: wca [speed 0..6]\n"); + printf("example: wca crushed 6 > log.txt\n"); + printf("if no speed is given speed 6 is assumed.\n"); + return 1; + } + +// if filename is provided try to open file, if possible: + + printf("trying to analyze %s...\n\n", argv[1]); + FILE * infile; + infile = fopen(argv[1], "rb"); + if (infile == NULL) + { + printf("can't open file!\n"); + exit(EXIT_FAILURE); + } + fseek(infile, 0, SEEK_END); + fsize = ftell(infile); + rewind(infile); + +// more error msgs if file is too large or too short to be a valid crushed binary + + if (fsize > maxsize) // if file too large + { + printf("file too large!\n"); + exit(EXIT_FAILURE); + } + + if (fsize < 4) + { + printf("nothing to analyze!\n"); + exit(EXIT_FAILURE); + } + if (argv[2] != NULL) + speed = atoi(argv[2]); + speed = speed * 8; + +// could be valid, so here we go: + + ssize_t bytes_read = fread(inbuf, 1, fsize&maxsize, infile); + (void) bytes_read; + fclose(infile); + tgtidx = inbuf[0] + 0x100 * inbuf[1]; + printf("start address: %d/%04X, size: %d bytes.\n\n", tgtidx, tgtidx, (int) fsize); + srcidx = 2; // skip start address + bitnum = 0; // to read 1st control byte right away + + do { + +//if end of either input data or output buffer reached before reading endmark: + + if (srcidx >= fsize || tgtidx >= maxsize) + { + printf("no valid endmark found, aborting.\n"); + exit(EXIT_FAILURE); + } + +// copy uncompressed bytes if control bit == 0, else repeat bytes: + + if (!getbit()) + { + len = 1; + while (!getbit() && len < maxsize) // get length of run from stopbit,bit... sequence + len = len * 2 + getbit(); + printf(" COPY %04X uncompressed bytes from %04X to %04X\n", len, srcidx, tgtidx); + + if (tgtidx + len > maxsize) // sanity check + len = maxsize - tgtidx; + if (srcidx + len > fsize) // sanity check + len = fsize&maxsize - srcidx; + + movedata(inbuf, outbuf, srcidx, tgtidx, len); + } + +// repeat a sequence of bytes that has already been decompressed: + + len = 1; + if (getbit()) // 3+ byte sequence + { + chunkofs = speed; + while (!getbit() && len < 0x100) // get len from stopbit,bit...sequence + len = len * 2 + getbit(); + len += 2; + } + else // 2+ byte sequence + { + chunkofs = speed + 4; + len++; + } + +// if reference length is valid repeat 2 or more bytes: + + if (len < 0x100) + { + offset = 0; + chunks = 2 * getbit() + getbit(); // get # of bitchunks (2 bits) + do { + chunkwidth = packbits[chunkofs + chunks]; // bitwidth of current chunk + while (chunkwidth--) + offset = 2 * offset + getbit(); //shift in offset bits of current chunk + if (chunks) // +1 if more bitchunks remaining + offset++; + } while (chunks--); + refidx = tgtidx - (offset + len); + printf(" REPEAT %02X bytes from %04X at %04X (offset %d)\n", len, refidx, tgtidx, offset); + + if (tgtidx + len > maxsize) // sanity check + len = maxsize - tgtidx; + movedata(outbuf, outbuf, refidx, tgtidx, len); + } + +// else depacker has reached end of input data (or crapped out somewhere before) + + else + { + printf(" endmark found (%d/%04X)\n", len-2, len-2); + if (srcidx < fsize) + printf("warning: endmark is not at end of input data!\n"); + } + + } while (len < 0x100); + return EXIT_SUCCESS; +} diff --git a/loader/tools/wcrush/wcrush/main.cpp b/loader/tools/wcrush/wcrush/main.cpp new file mode 100644 index 0000000..f4d5252 --- /dev/null +++ b/loader/tools/wcrush/wcrush/main.cpp @@ -0,0 +1,283 @@ +// wcrush v0.3b by CS - a replacement for Taboo's level crusher which unfornately doesn't run in 64bit Windows. + +//#include was added by Visual Studio 2013, remove or change to whatever your dev-tools want instead + +#include +#include +#include + +#define maxsize 65535 +#define endmark 511 +#define maxref 255 + +uint8_t inbuf[maxsize + 1]; //our working buffers +uint8_t outbuf[maxsize + 258]; //+ some extra bytes, just in case +uint32_t srcidx, tgtidx, ctrlidx, uncmp, ctrlbit; + +void putbit (uint32_t bit); +void storelenbits (uint32_t l, uint32_t stopbit); +void storeoffset (uint32_t offset, uint32_t table); +void storeref(uint32_t offset, uint32_t size, uint32_t table); +void copybytes (); +uint32_t packed (uint32_t end, int speed); + +int main(int argc, char* argv[]) +{ + printf ("\ncrush for windows v0.3b by CS\n\n"); + int speed = 7; + if (argv[1] != NULL) + speed = atoi(argv[1]); + if (argc < 3 || speed < 0 || speed > 6) // not the expected amount/range of parameters + { + printf ("usage: wcrush [target name]\n"); + exit (EXIT_FAILURE); // not really an error, but returning success only when something got packed seems useful. + } + + printf ("trying to crush %s...\n\n", argv[2]); + FILE * infile; + infile = fopen (argv[2], "rb"); + if (infile == NULL) // file can't be opened for whatever reason: + { + printf ("can't open %s!\n", argv[2]); + exit (EXIT_FAILURE); + } + + fseek (infile, 0, SEEK_END); + uint64_t filesize = ftell(infile); + rewind (infile); + if (filesize > maxsize) // if file too large + { + printf ("file too large!\n"); + exit (EXIT_FAILURE); + } + if (filesize < 4) // or too small + { + printf("file too small!\n"); + exit(EXIT_FAILURE); + } + + uint16_t ifsize = filesize & 0xffff; + ssize_t bytes_read = fread (inbuf, 1, ifsize, infile); + (void) bytes_read; + fclose (infile); + printf ("size of %s: %d bytes.\n", argv[2], ifsize); + + uint16_t ofsize = packed (ifsize, speed); + + if (!ofsize) // something went wrong + { + printf("couldn't pack file!\n"); + exit(EXIT_FAILURE); + } + + outbuf[0] = inbuf[0]; // copy startaddress + outbuf[1] = inbuf[1]; + + printf("crushed size: %d bytes, %d%% left\n", ofsize, 100 * ofsize / ifsize); + FILE * outfile; + if (argv[3]!=NULL) + outfile = fopen(argv[3], "wb"); + else + outfile = fopen("crushed", "wb"); + if (outfile == NULL) // if file can't be opened for whatever reason: + { + printf ("can't open %s!\n", argv[2]); + exit (EXIT_FAILURE); + } + + fwrite (outbuf, 1, ofsize, outfile); + fclose (outfile); + return EXIT_SUCCESS; +} + +uint32_t packed(uint32_t end, int speed) +{ + const uint16_t longmax[7] = { 115, 343, 679, 1359, 2383, 4687 , 9359 }; + const uint16_t shortmax[7] = { 115, 343, 679, 1359, 1359, 1359, 1359 }; + uint16_t previous[maxsize + 1]; + uint16_t last[256]; + uint32_t refidx, distance, bestoffs, bestlen, len, max, table, + minlongdst, maxshrtdst, maxlongdst; + + // walk the input data once and create a helper table containing links to the + // closest match for the byte at current position to speed up packing a bit. + + for (refidx = 0; refidx < 256; refidx++) // no bytevalue has been found yet + last[refidx] = 0; + for (refidx = 0; refidx < end; refidx++) + { + previous[refidx] = last[inbuf[refidx]]; + last[inbuf[refidx]] = refidx; + } + + table = speed * 8; // some speed dependant settings + minlongdst = longmax[speed]; + maxshrtdst = shortmax[speed]; + maxlongdst = minlongdst + maxref; + + srcidx = 2; // skip start adress + ctrlidx = 2; // dito + tgtidx = 3; // to avoid overwriting the 1st contol byte + uncmp = 0; // no uncompressable sequence found yet + ctrlbit = 128; // first control bit value + + // now comes the (very naively designed) compression loop: + + while (srcidx < end && tgtidx <= maxsize) + { + bestlen = 0; // no match found yet at this position + refidx = srcidx; + + while ((refidx=previous[refidx]) >= 2 && (distance=srcidx-refidx) <= maxlongdst && bestlen < maxref) + { + if (refidx) // quickly skip ahead if not even a single byte match exists + { + len = 1; + if (distance < maxref) // match must not overlap with output data + max = distance; + else // and can't be more than 255 bytes long. + max = maxref; + if (srcidx + max > end) + max = end - srcidx; // and must no go beyond end of input data + + while (len < max && inbuf[refidx + len] == inbuf[srcidx + len]) + len++; + + if (len > bestlen && distance - len <= minlongdst) + { + bestlen = len; + bestoffs = distance - len; + } + } + } + + if (bestlen > 2 || (bestlen == 2 && bestoffs <= maxshrtdst)) + storeref(bestoffs, bestlen, table); // if a match of 2 or more bytes was found + else + { + uncmp++; + srcidx++; + } + } + + if (srcidx > end || tgtidx > maxsize) + return 0; // if something went wrong + else + { + if (uncmp) // store any remaining uncompressable bytes + copybytes(); + else + putbit(1); //or flag reference coming up + putbit(1); // flag 3+ byte length + storelenbits(endmark, 0); + while (ctrlbit) // make sure the last control bits are written out + putbit(0); + return tgtidx; + } +} + +// encode a 2 or more bytes long repetition into output data + +void storeref(uint32_t offset, uint32_t size, uint32_t table) +{ + if (uncmp) // if there's a run of incompressible bytes to encode first + copybytes (); + else + putbit(1); // signal repetition ahead + if (size == 2) + { + putbit(0); // flag length = 2 + storeoffset(offset, table + 4); + } + else + { + putbit(1); // flag length = 3 or more + if (size == 3) + putbit(1); //only stopbit required + else + storelenbits(size - 2, 1); + storeoffset(offset, table); + } + srcidx += size; // skip the input data that is repeated +} + +void storeoffset(uint32_t offset, uint32_t table) +{ + const uint8_t packbits[56] = { + 2, 2, 1, 1, 2, 2, 1, 1, // worst compression (speed 0) + 3, 1, 2, 2, 3, 1, 2, 2, + 3, 2, 2, 2, 3, 2, 2, 2, + 4, 2, 2, 2, 4, 2, 2, 2, + 4, 2, 2, 3, 4, 2, 2, 2, + 4, 2, 3, 3, 4, 2, 2, 2, + 4, 3, 3, 3, 4, 2, 2, 2 }; // best compression (speed 6) + + uint16_t bits = 0; + uint16_t largest = 0; + int16_t chunk = -1; // required to end up with the right amount of chunks after calculation + uint16_t subst = 0; + while (offset >= largest) // while can't be stored with current amount of bits: + { + subst = largest; //for offset correction + bits += packbits[++chunk + table]; //add another bitchunk + largest += (1 << bits); // 2^packbits[chunk0]+2^(bits+packbits[chunk1])+... + } + offset -= subst; // cause depacker adds 1 to partial result for every extra bitchunk + putbit(chunk & 2); + putbit(chunk & 1); + bits = 1 << bits; + while (bits /= 2) + putbit(offset&bits); +} + +// copy an uncompressable byte sequence from input to output data: + +void copybytes() +{ + putbit(0); // flag run of uncompressed bytes + storelenbits(uncmp, 1); + while (uncmp) + outbuf[tgtidx++] = inbuf[srcidx - uncmp--]; +} + +// this will create the stopbit/bit sequence which the unpacker uses to calculate lengths. +// final stopbit is an extra parameter as the end mark doesn't need it. +// the twobyte-flag is also NOT included, add it manually BEFORE calling this function. +// apart from that it actually works, so note to me: DON'T TOUCH IT ANYMORE! + +void storelenbits(uint32_t len, uint32_t stopbit) +{ + uint16_t msbval = 1; + uint16_t tmp = len; + while (tmp /= 2) + msbval *= 2; //find out msb of length + tmp = len - msbval; //then remove it, see unpacker mechanics for details + while (msbval /= 2) // store remaining length bits: + { + putbit(0); // stopbit 0 to signal another length bit is following + putbit(tmp & msbval); // shift in length bit + } + if (stopbit) + putbit(1); +} + +// collect control bits and store them at the next unused outbuf position. +// IMPORTANT: The correct order of operations is to not add the control byte +// to output data when it is full but when it is full AND another bit should +// be added! The difference is subtle and may or may not derail the depacker, +// depending on the payload, so when errors occur after depacking in a rather +// random fashion make sure to check this routine first: + +void putbit(uint32_t bit) +{ + + if (!ctrlbit) // byte full? + { + ctrlidx = tgtidx++; + outbuf[ctrlidx] = 0; // reinit control byte + ctrlbit = 128; + } + if (bit) + outbuf[ctrlidx] |= ctrlbit; + ctrlbit /= 2; +} \ No newline at end of file diff --git a/loader/version.inc b/loader/version.inc new file mode 100644 index 0000000..e0df588 --- /dev/null +++ b/loader/version.inc @@ -0,0 +1,2 @@ +.define REPOSITORY_VERSION 194 +.define REPOSITORY_VERSION_STRING "194" diff --git a/shared/basic.inc b/shared/basic.inc new file mode 100755 index 0000000..ee5b0c5 --- /dev/null +++ b/shared/basic.inc @@ -0,0 +1,133 @@ + +.ifndef _BASIC_INC_ +_BASIC_INC_ = 1 + +BASIC_ROM = $a000 +BASIC_ROM_SIZE = $2000 + +LINNUM = $14 + +FBUFFR = $0100; floating point string buffer written by FOUT + +TOKEN_SYS = $9e + +PETSCII_RETURN = $0d +PETSCII_LO_UP_CASE = $0e +PETSCII_BLINK = $0f; C128 80 columns +PETSCII_CLEAR = $93 + +PETSCII_BLACK = $90 +PETSCII_WHITE = $05 +PETSCII_RED = $1c +PETSCII_CYAN = $9f +PETSCII_PURPLE = $9c +PETSCII_GREEN = $1e +PETSCII_BLUE = $1f +PETSCII_YELLOW = $9e +PETSCII_ORANGE = $81 +PETSCII_BROWN = $95 +PETSCII_LIGHTRED = $96 +PETSCII_DARKGREY = $97 +PETSCII_GREY = $98 +PETSCII_LIGHTGREEN = $99 +PETSCII_LIGHTBLUE = $9a +PETSCII_LIGHTGREY = $9b + +.if .defined(PLATFORM) & (PLATFORM = 16) + +NEW = $8a7b; Perform NEW +CLR = $8a9a; PErform CLR +STROUT = $9088; Output String +GIVAYF = $9471; Convert Integer in (AC/YR) to FAC +NORMALIZE = $952b; Normalize FAC#1 +INT24TOMANTISSA = $9534; Integer in (YR/XR/AR) to FAC mantissa MSBs +N32768 = $986c; Constant -32768 in floating point +FLPINT = $9871; FAC#1 to Integer in A/Y +AYINT = $9886; FAC#1 to Positive Integer +STRLIT = $9b74; Set Up String +FADDH = $a062; Add 0.5 to FAC#1 +FADD = $a066; Perform Addition +FSUB = $a06c; Perform Subtraction +FMULT = $a078; Perform Multiply of float in (AC/YR) with FAC +CONUPK = $a0dc; Move floating point number from memory to FAC#2 +MUL10 = $a162; Multiply FAC#1 by 10 +TENC = $a179; Constant 10 in floating point +DIV10 = $a183; Divide FAC#1 by 10 +FDIVM = $a18c; Divide FAC#2 by float in (AC/YR), result in FAC#1 +FDIV = $a194; Divide float in (AC/YR) by FAC#1, result in FAC#1 +FDIVT = $a197; Divide FAC#2 by FAC#1, result in FAC#1 +MOVFM = $a221; Move floating point number from memory to FAC#1 +MOV2M = $a259; Store FAC#1 in memory at (XR/YR) +MOVAF = $a291; Copy FAC#1 to FAC#2 +LINPRT = $a45f; Print integer in A/X +FOUT = $a46f; Convert FAC#1 to ASCII String + +.elseif .defined(PLATFORM) & (PLATFORM = 128) + +NEW = $51d9; Perform NEW +CLR = $51f8; Perform CLR +STROUT = $55e2; Output String +NORMALIZE = $7a14; Normalize FAC#1 +INT24TOMANTISSA = $7a1d; Integer in (YR/XR/AR) to FAC#1 mantissa MSBs +FLPINT = $849f; FAC#1 to Integer in A/Y +GIVAYF = $84c9; Convert Integer in (AC/YR) to FAC +STRLIT = $869a; Set Up String +FADDH = $8a0e; Add 0.5 to FAC#1 +FMULT = $8a08; Perform Multiply of float in (AC/YR) with FAC +FDIV = $8a1e; Divide float in (AC/YR) by FAC#1, result in FAC#1 +CONUPK = $8a89; Move floating point number from memory to FAC#2 +MUL10 = $8b17; Multiply FAC#1 by 10 +DIV10 = $8b38; Divide FAC#1 by 10 +FDIVM = $8b3f; Divide FAC#2 by float in (AC/YR), result in FAC#1 +FDIVT = $8b4c; Divide FAC#2 by FAC#1, result in FAC#1 +MOVFM = $8bd4; Move floating point number from memory to FAC#1 +MOV2M = $8c00; Store FAC#1 in memory at (XR/YR) +MOVAF = $8c38; Copy FAC#1 to FAC#2 +LINPRT = $8e32; Print integer in A/X +FOUT = $8e42; Convert FAC#1 to ASCII String + +.else + +NEW = $a644; Perform NEW +CLR = $a660; Perform CLR +STROUT = $ab1e; Output String +NORMALIZE = $af7e; Normalize FAC#1 +INT24TOMANTISSA = $af87; GETTIM + 3, Integer in (YR/XR/AR) to FAC#1 mantissa MSBs +N32768 = $b1a5; Constant -32768 in floating point +FLPINT = $b1aa; FAC#1 to Integer in A/Y +AYINT = $b1bf; FAC#1 to Positive Integer +GIVAYF = $b391; Convert Integer in (AC/YR) to FAC +STRLIT = $b487; Set Up String +FADDH = $b849; Add 0.5 to FAC#1 +FSUB = $b850; Perform Subtraction +FADD = $b867; Perform Addition +FMULT = $ba28; Perform Multiply of float in (AC/YR) with FAC +CONUPK = $ba8c; Move floating point number from memory to FAC#2 +MUL10 = $bae2; Multiply FAC#1 by 10 +TENC = $baf9; Constant 10 in floating point +DIV10 = $bafe; Divide FAC#1 by 10 +FDIVM = $bb05; Divide FAC#2 by float in (AC/YR), result in FAC#1 +FDIV = $bb0f; Divide float in (AC/YR) by FAC#1, result in FAC#1 +FDIVT = $bb12; Divide FAC#2 by FAC#1, result in FAC#1 +MOVFM = $bba2; Move floating point number from memory to FAC#1 +MOV2M = $bbd4; Store FAC#1 in memory at (XR/YR) +MOVAF = $bc0c; Copy FAC#1 to FAC#2 +LINPRT = $bdcd; Print integer in A/X +FOUT = $bddd; Convert FAC#1 to ASCII String + +PIVAL = $aea8; Constant PI in floating point +GETADR = $b7f7; Convert FAC to Integer in LINNUM +FONE = $b9bc; Floating point constant: 1 +.endif + +.macro LOAD_ADDRESS + .word * + $02 +.endmacro; LOAD_ADDRESS + +.macro BASIC_STARTLINE linenumber + .assert * = $0801, warning, "***** PC is not $0801 *****" + .word $080d, linenumber + .byte TOKEN_SYS, "2061", $00, $00, $00 +.endmacro; BASIC_STARTLINE + +.endif; !_BASIC_INC_ diff --git a/shared/cia.inc b/shared/cia.inc new file mode 100755 index 0000000..f54f59a --- /dev/null +++ b/shared/cia.inc @@ -0,0 +1,429 @@ +; MOS6526 + +.ifndef _VIA_INC_ +.ifndef _CIA_INC_ +_CIA_INC_ = 1 + + +.if (::PLATFORM = 64) | (::PLATFORM = 128) ; cia.inc defines C-64 and 1581 symbols, so it might be included along with, e.g., ted.inc +; C-64 keyboard matrix positions +STOP_COLUMN = %01111111 +STOP_ROW = %01111111 +LEFT_SHIFT_COLUMN = %11111101 +LEFT_SHIFT_ROW = %01111111 +ALL_COLUMNS = %00000000 +NO_ROWS = %11111111 +.endif + + +; the two CIAs in the C-64 + +CIA1_BASE = $dc00 + +CIA_PIO0 = %00000001 +CIA_PIO1 = %00000010 +CIA_PIO2 = %00000100 +CIA_PIO3 = %00001000 +CIA_PIO4 = %00010000 +CIA_PIO5 = %00100000 +CIA_PIO6 = %01000000 +CIA_PIO7 = %10000000 + +CIA1_PRA = CIA1_BASE + $00; Port register A + KBD_COLUMN0 = ~CIA_PIO0 + KBD_COLUMN1 = ~CIA_PIO1 + KBD_COLUMN2 = ~CIA_PIO2 + KBD_COLUMN3 = ~CIA_PIO3 + KBD_COLUMN4 = ~CIA_PIO4 + KBD_COLUMN5 = ~CIA_PIO4 + KBD_COLUMN6 = ~CIA_PIO5 + KBD_COLUMN7 = ~CIA_PIO5 + + JOY2_UP = ~CIA_PIO0 + JOY2_DOWN = ~CIA_PIO1 + JOY2_LEFT = ~CIA_PIO2 + JOY2_RIGHT = ~CIA_PIO3 + JOY2_FIRE = ~CIA_PIO4 + + PADDLE_ENPORT1 = CIA_PIO6 + PADDLE_ENPORT2 = CIA_PIO7 + PADDLE_EN_MASK = CIA_PIO5 | CIA_PIO4 | CIA_PIO3 | CIA_PIO2 | CIA_PIO1 | CIA_PIO0 + +CIA1_PRB = CIA1_BASE + $01; Port register B + KBD_ROW0 = ~CIA_PIO0 + KBD_ROW1 = ~CIA_PIO1 + KBD_ROW2 = ~CIA_PIO2 + KBD_ROW3 = ~CIA_PIO3 + KBD_ROW4 = ~CIA_PIO4 + KBD_ROW5 = ~CIA_PIO5 + KBD_ROW6 = ~CIA_PIO6 + KBD_ROW7 = ~CIA_PIO7 + + JOY1_UP = ~CIA_PIO0 + JOY1_DOWN = ~CIA_PIO1 + JOY1_LEFT = ~CIA_PIO2 + JOY1_RIGHT = ~CIA_PIO3 + JOY1_FIRE = ~CIA_PIO4 + +CIA1_DDRA = CIA1_BASE + $02; Data direction register A + CIA_PIO0_OUTPUT = CIA_PIO0 + CIA_PIO0_INPUT = %00000000 + CIA_PIO1_OUTPUT = CIA_PIO1 + CIA_PIO1_INPUT = %00000000 + CIA_PIO2_OUTPUT = CIA_PIO2 + CIA_PIO2_INPUT = %00000000 + CIA_PIO3_OUTPUT = CIA_PIO3 + CIA_PIO3_INPUT = %00000000 + CIA_PIO4_OUTPUT = CIA_PIO4 + CIA_PIO4_INPUT = %00000000 + CIA_PIO5_OUTPUT = CIA_PIO5 + CIA_PIO5_INPUT = %00000000 + CIA_PIO6_OUTPUT = CIA_PIO6 + CIA_PIO6_INPUT = %00000000 + CIA_PIO7_OUTPUT = CIA_PIO7 + CIA_PIO7_INPUT = %00000000 + +CIA1_DDRB = CIA1_BASE + $03; Data direction register B + +CIA1_TA_LO = CIA1_BASE + $04; Timer A, low byte +CIA1_TA_HI = CIA1_BASE + $05; Timer A, high byte +CIA1_TB_LO = CIA1_BASE + $06; Timer B, low byte +CIA1_TB_HI = CIA1_BASE + $07; Timer B, high byte + + CIA1_TOD_10S = CIA1_BASE + $08; Time of day, 1/10 seconds + CIA1_TOD_SEC = CIA1_BASE + $09; Time of day, seconds + CIA1_TOD_MIN = CIA1_BASE + $0a; Time of day, minutes + CIA1_TOD_HRS = CIA1_BASE + $0b; Time of day, hours + TOD_AMPM = %10000000 + TOD_AM = %00000000 + TOD_PM = %10000000 + TOD_HRS_MASK = %00011111 + +CIA1_SDR = CIA1_BASE + $0c; Serial data register + +CIA1_ICR = CIA1_BASE + $0d; Interrupt control register + TIMERA_IRQ = %00000001 + TIMERB_IRQ = %00000010 + TOD_IRQ = %00000100 + SERIAL_IRQ = %00001000 + FLAG1_IRQ = %00010000 + EVERY_IRQ = %01111111 + CIA_INTERRUPT = %10000000 + CIA_CLR_INTF = %00000000 + CIA_SET_INTF = %10000000 + +CIA1_CRA = CIA1_BASE + $0e; Control Register A + TIMER_STOP = %00000000 + TIMER_START = %00000001 + OUTPUT_TO_PB6 = %00000010 + OUTPUT_PULSE = %00000000 + OUTPUT_TOGGLE = %00000100 + CONTINUOUS = %00000000 + ONE_SHOT = %00001000 + FORCE_LOAD = %00010000 + COUNT_PHI2 = %00000000 + COUNT_CNT = %00100000 + IOMODE_INPUT = %00000000 + IOMODE_OUTPUT = %01000000 + TOD_FREQ_60HZ = %00000000 + TOD_FREQ_50HZ = %10000000 + TOD_FREQ_MASK = TOD_FREQ_50HZ + +CIA1_CRB = CIA1_BASE + $0f; Control Register B +;TIMER_STOP = %00000000 +;TIMER_START = %00000001 + OUTPUT_TO_PB7 = %00000010 +;OUTPUT_PULSE = %00000000 +;OUTPUT_TOGGLE = %00000100 +;CONTINUOUS = %00000000 +;ONE_SHOT = %00001000 +;FORCE_LOAD = %00010000 +;COUNT_PHI2 = %00000000 + COUNT_CNT_POS = %00100000 + COUNT_TA_UNDF = %01000000 + COUNT_TA_UFCN = %01100000 + COUNT_CRB_MASK = COUNT_TA_UFCN + TOD_SET_CLOCK = %00000000 + TOD_SET_ALARM = %10000000 + + +CIA2_BASE = $dd00 + +;CIA_PIO0 = %00000001 +;CIA_PIO1 = %00000010 +;CIA_PIO2 = %00000100 +;CIA_PIO3 = %00001000 +;CIA_PIO4 = %00010000 +;CIA_PIO5 = %00100000 +;CIA_PIO6 = %01000000 +;CIA_PIO7 = %10000000 + +CIA2_PRA = CIA2_BASE + $00; Port register A + VIC2_BANK = %00000011 + VIC2_BANK0 = %00000000 + VIC2_BANK1 = CIA_PIO0 + VIC2_BANK2 = CIA_PIO1 + VIC2_BANK3 = CIA_PIO1 | CIA_PIO0 + VIC2_BANK_SHIFT = 0 + VIC2_BANK_MASK = VIC2_BANK + .define VIC2_MAKE_BANK(address) .lobyte(((address & $c000) >> 14) ^ 3) + RS232_TXD = CIA_PIO2 + SERIAL_ATN_OUT = CIA_PIO3 + SERIAL_CLK_OUT = CIA_PIO4 + SERIAL_DATA_OUT = CIA_PIO5 + SERIAL_CLK_IN = CIA_PIO6 + SERIAL_DATA_IN = CIA_PIO7 + +CIA2_PRB = CIA2_BASE + $01; Port register B + RS232_RXD = CIA_PIO0 + RS232_RTS = CIA_PIO1 + RS232_DTR = CIA_PIO2 + RS232_RING = CIA_PIO3 + RS232_CARRIER = CIA_PIO4 + USERPORT_PB5 = CIA_PIO5 + RS232_CTS = CIA_PIO6 + RS232_DSR = CIA_PIO7 + +CIA2_DDRA = CIA2_BASE + $02; Data direction register A +;CIA_PIO0_OUTPUT = CIA_PIO0 +;CIA_PIO0_INPUT = %00000000 +;CIA_PIO1_OUTPUT = CIA_PIO1 +;CIA_PIO1_INPUT = %00000000 + CIA_VIC2_BANK_OUTPUT = CIA_PIO0 | CIA_PIO1 + CIA_VIC2_BANK_INPUT = %00000000 +;CIA_PIO2_OUTPUT = CIA_PIO2 +;CIA_PIO2_INPUT = %00000000 + CIA_RS232_TXD_OUTPUT = CIA_PIO2 + CIA_RS232_TXD_INPUT = %00000000 +;CIA_PIO3_OUTPUT = CIA_PIO3 +;CIA_PIO3_INPUT = %00000000 + CIA_SERIAL_ATN_OUT_OUTPUT = CIA_PIO3 + CIA_SERIAL_ATN_OUT_INPUT = %00000000 +;CIA_PIO4_OUTPUT = CIA_PIO4 +;CIA_PIO4_INPUT = %00000000 + CIA_SERIAL_CLK_OUT_OUTPUT = CIA_PIO4 + CIA_SERIAL_CLK_OUT_INPUT = %00000000 +;CIA_PIO5_OUTPUT = CIA_PIO5 +;CIA_PIO5_INPUT = %00000000 + CIA_SERIAL_DATA_OUT_OUTPUT = CIA_PIO5 + CIA_SERIAL_DATA_OUT_INPUT = %00000000 +;CIA_PIO6_OUTPUT = CIA_PIO6 +;CIA_PIO6_INPUT = %00000000 + CIA_SERIAL_CLK_IN_OUTPUT = CIA_PIO6 + CIA_SERIAL_CLK_IN_INPUT = %00000000 +;CIA_PIO7_OUTPUT = CIA_PIO7 +;CIA_PIO7_INPUT = %00000000 + CIA_SERIAL_DATA_IN_OUTPUT = CIA_PIO7 + CIA_SERIAL_DATA_IN_INPUT = %00000000 + + +CIA2_DDRB = CIA2_BASE + $03; Data direction register B + +CIA2_TA_LO = CIA2_BASE + $04; Timer A, low byte +CIA2_TA_HI = CIA2_BASE + $05; Timer A, high byte +CIA2_TB_LO = CIA2_BASE + $06; Timer B, low byte +CIA2_TB_HI = CIA2_BASE + $07; Timer B, high byte + +CIA2_TOD_10S = CIA2_BASE + $08; Time of day, 1/10 seconds +CIA2_TOD_SEC = CIA2_BASE + $09; Time of day, seconds +CIA2_TOD_MIN = CIA2_BASE + $0a; Time of day, minutes +CIA2_TOD_HRS = CIA2_BASE + $0b; Time of day, hours +;TOD_AMPM = %10000000 +;TOD_AM = %00000000 +;TOD_PM = %10000000 +;TOD_HRS_MASK = %00011111 + +CIA2_SDR = CIA2_BASE + $0c; Serial data register + +CIA2_ICR = CIA2_BASE + $0d; Interrupt control register +TIMERA_NMI = %00000001 +TIMERB_NMI = %00000010 +TOD_NMI = %00000100 +SERIAL_NMI = %00001000 +FLAG1_NMI = %00010000 +EVERY_NMI = %01111111 +;CIA_INTERRUPT = %10000000 +;CIA_CLR_INTF = %00000000 +;CIA_SET_INTF = %10000000 + +CIA2_CRA = CIA2_BASE + $0e; Control Register A +;TIMER_STOP = %00000000 +;TIMER_START = %00000001 +;OUTPUT_TO_PB6 = %00000010 +;OUTPUT_PULSE = %00000000 +;OUTPUT_TOGGLE = %00000100 +;CONTINUOUS = %00000000 +;ONE_SHOT = %00001000 +;FORCE_LOAD = %00010000 +;COUNT_PHI2 = %00000000 +;COUNT_CNT = %00100000 + SERIAL_INPUT = %00000000 + SERIAL_OUTPUT = %01000000 +;TOD_FREQ_60HZ = %00000000 +;TOD_FREQ_50HZ = %10000000 + +CIA2_CRB = CIA2_BASE + $0f; Control Register B +;TIMER_STOP = %00000000 +;TIMER_START = %00000001 +;OUTPUT_TO_PB7 = %00000010 +;OUTPUT_PULSE = %00000000 +;OUTPUT_TOGGLE = %00000100 +;CONTINUOUS = %00000000 +;ONE_SHOT = %00001000 +;FORCE_LOAD = %00010000 +;COUNT_PHI2 = %00000000 +;COUNT_CNT_POS = %00100000 +;COUNT_TA_UNDF = %01000000 +;COUNT_TA_UFCN = %01100000 +;COUNT_CRB_MASK = COUNT_TA_UFCN +;TOD_SET_CLOCK = %00000000 +;TOD_SET_ALARM = %10000000 + + +; the CIA in the 1571 (not CR) and 1581 +; on 1571, only the fast serial interface is connected +; on 1571CR, only the fast serial interface is implemented (MOS5710) + +CIA_BASE = $4000 + +;CIA_PIO0 = %00000001 +;CIA_PIO1 = %00000010 +;CIA_PIO2 = %00000100 +;CIA_PIO3 = %00001000 +;CIA_PIO4 = %00010000 +;CIA_PIO5 = %00100000 +;CIA_PIO6 = %01000000 +;CIA_PIO7 = %10000000 + +CIA_PRA = CIA_BASE + $00; Port register A + SIDE_SELECT = CIA_PIO0 + DISK_INSERTED = CIA_PIO1 + DISK_IS_INSERTED = %00000000 + NO_DISK_IS_INSERTED = DISK_INSERTED + MOTOR = CIA_PIO2 + MOTOR_ON = %00000000 + MOTOR_OFF = MOTOR + DEVICE_NUMBER = CIA_PIO3 | CIA_PIO4 + DEVICE_NUMBER_8 = %00000000 + DEVICE_NUMBER_9 = CIA_PIO3 + DEVICE_NUMBER_10 = CIA_PIO4 + DEVICE_NUMBER_11 = CIA_PIO3 | CIA_PIO4 + DEVICE_NUMBER_SHIFT = 3 + DEVICE_NUMBER_MASK = DEVICE_NUMBER + POWER_LED = CIA_PIO5 + DRIVE_LED = CIA_PIO6 + DISK_CHANGE = CIA_PIO7 + DISK_CHANGED = %00000000 + NO_DISK_CHANGED = DISK_CHANGE + +CIA_PRB = CIA_BASE + $01; Port register B + DATA_IN = CIA_PIO0 + DATA_OUT = CIA_PIO1 + CLK_IN = CIA_PIO2 + CLK_OUT = CIA_PIO3 + ATNA_ENABLE_OUT = CIA_PIO4 + FSM_BUS_DRIVER_DIRECTION = CIA_PIO5 + FSM_BUS_DRIVER_INPUT = %00000000 + FSM_BUS_DRIVER_OUTPUT = FSM_BUS_DRIVER_DIRECTION + WRITE_PROTECT = CIA_PIO6 + ATN_IN = CIA_PIO7 + +CIA_DDRA = CIA_BASE + $02; Data direction register A +;CIA_PIO0_OUTPUT = CIA_PIO0 +;CIA_PIO0_INPUT = %00000000 +;CIA_PIO1_OUTPUT = CIA_PIO1 +;CIA_PIO1_INPUT = %00000000 +;CIA_PIO2_OUTPUT = CIA_PIO2 +;CIA_PIO2_INPUT = %00000000 +;CIA_PIO3_OUTPUT = CIA_PIO3 +;CIA_PIO3_INPUT = %00000000 +;CIA_PIO4_OUTPUT = CIA_PIO4 +;CIA_PIO4_INPUT = %00000000 +;CIA_PIO5_OUTPUT = CIA_PIO5 +;CIA_PIO5_INPUT = %00000000 +;CIA_PIO6_OUTPUT = CIA_PIO6 +;CIA_PIO6_INPUT = %00000000 +;CIA_PIO7_OUTPUT = CIA_PIO7 +;CIA_PIO7_INPUT = %00000000 + +CIA_DDRB = CIA_BASE + $03; Data direction register B + DATA_IN_OUTPUT = CIA_PIO0 + DATA_IN_INPUT = %00000000 + DATA_OUT_OUTPUT = CIA_PIO1 + DATA_OUT_INPUT = %00000000 + CLK_IN_OUTPUT = CIA_PIO2 + CLK_IN_INPUT = %00000000 + CLK_OUT_OUTPUT = CIA_PIO3 + CLK_OUT_INPUT = %00000000 + ATNA_ENABLE_OUT_OUTPUT = CIA_PIO4 + ATNA_ENABLE_OUT_INPUT = %00000000 + FSM_BUS_DRIVER_DIRECTION_OUTPUT = CIA_PIO5 + FSM_BUS_DRIVER_DIRECTION_INPUT = %00000000 + WRITE_PROTECT_OUTPUT = CIA_PIO6 + WRITE_PROTECT_INPUT = %00000000 + CIA_ATN_IN_OUTPUT = CIA_PIO7 + CIA_ATN_IN_INPUT = %00000000 + +CIA_TA_LO = CIA_BASE + $04; Timer A, low byte +CIA_TA_HI = CIA_BASE + $05; Timer A, high byte +CIA_TB_LO = CIA_BASE + $06; Timer B, low byte +CIA_TB_HI = CIA_BASE + $07; Timer B, high byte + +CIA_TOD_10S = CIA_BASE + $08; Time of day, 1/10 seconds +CIA_TOD_SEC = CIA_BASE + $09; Time of day, seconds +CIA_TOD_MIN = CIA_BASE + $0a; Time of day, minutes +CIA_TOD_HRS = CIA_BASE + $0b; Time of day, hours +;TOD_AMPM = %10000000 +;TOD_AM = %00000000 +;TOD_PM = %10000000 +;TOD_HRS_MASK = %00011111 + +CIA_SDR = CIA_BASE + $0c; Serial data register + +CIA_ICR = CIA_BASE + $0d; Interrupt control register +;TIMERA_IRQ = %00000001 +;TIMERB_IRQ = %00000010 +;TOD_IRQ = %00000100 +;SERIAL_IRQ = %00001000 +;FLAG1_IRQ = %00010000 +;EVERY_IRQ = %01111111 +;CIA_INTERRUPT = %10000000 +;CIA_CLR_INTF = %00000000 +;CIA_SET_INTF = %10000000 + +CIA_CRA = CIA_BASE + $0e; Control Register A +;TIMER_STOP = %00000000 +;TIMER_START = %00000001 +;OUTPUT_TO_PB6 = %00000010 +;OUTPUT_PULSE = %00000000 +;OUTPUT_TOGGLE = %00000100 +;CONTINUOUS = %00000000 +;ONE_SHOT = %00001000 +;FORCE_LOAD = %00010000 +;COUNT_PHI2 = %00000000 +;COUNT_CNT = %00100000 +;IOMODE_INPUT = %00000000 +;IOMODE_OUTPUT = %01000000 +;TOD_FREQ_60HZ = %00000000 +;TOD_FREQ_50HZ = %10000000 +;TOD_FREQ_MASK = TOD_FREQ_50HZ + +CIA_CRB = CIA_BASE + $0f; Control Register B +;TIMER_STOP = %00000000 +;TIMER_START = %00000001 +;OUTPUT_TO_PB7 = %00000010 +;OUTPUT_PULSE = %00000000 +;OUTPUT_TOGGLE = %00000100 +;CONTINUOUS = %00000000 +;ONE_SHOT = %00001000 +;FORCE_LOAD = %00010000 +;COUNT_PHI2 = %00000000 +;COUNT_CNT_POS = %00100000 +;COUNT_TA_UNDF = %01000000 +;COUNT_TA_UFCN = %01100000 +;COUNT_CRB_MASK = COUNT_TA_UFCN +;TOD_SET_CLOCK = %00000000 +;TOD_SET_ALARM = %10000000 + +.endif; !_CIA_INC_ +.endif; !_VIA_INC_ diff --git a/shared/cpu.inc b/shared/cpu.inc new file mode 100755 index 0000000..4c109c5 --- /dev/null +++ b/shared/cpu.inc @@ -0,0 +1,410 @@ +; MOS6502/6510/8500/8502 + +.ifndef _CPU_INC_ +_CPU_INC_ = 1 + +.ifndef PLATFORM +PLATFORM = 64 +.endif + +.if PLATFORM = 16 +CLOCK_PAL = 886723; Hz - single clock +.define CYCLES_PER_SECOND_PAL .byte $c3, $87, $0d, $00; 886723 - single clock +CLOCK_NTSC = 894886; Hz - single clock +.define CYCLES_PER_SECOND_NTSC .byte $a6, $a7, $0d, $00; 894886 - single clock +.else +CLOCK_PAL = 985248; Hz +.define CYCLES_PER_SECOND_PAL .byte $a0, $08, $0f, $00; 985248 +CLOCK_NTSC = 1022727; Hz +.define CYCLES_PER_SECOND_NTSC .byte $07, $9b, $0f, $00; 1022727 +.endif + +OPC_BRK = $00 +OPC_ORA_ZPXI = $01 +OPC_JAM = $02; unofficial +OPC_SLO_ZPXI = $03; unofficial +;OPC_NOP = $04; unofficial +OPC_ORA_ZP = $05 +OPC_ASL_ZP = $06 +OPC_SLO_ZP = $07; unofficial +OPC_PHP = $08 +OPC_ORA_IMM = $09 +OPC_ASL = $0a +OPC_ANC_IMM = $0b; unofficial +OPC_NOP_ABS = $0c +OPC_ORA_ABS = $0d +OPC_ASL_ABS = $0e +OPC_SLO_ABS = $0f; unofficial +OPC_BPL = $10 +OPC_ORA_ZPIY = $11 +;OPC_JAM = $12; unofficial +OPC_SLO_ZPIY = $13; unofficial +OPC_NOP_ZPX = $14; unofficial +OPC_ORA_ZPX = $15 +OPC_ASL_ZPX = $16 +OPC_SLO_ZPX = $17; unofficial +OPC_CLC = $18 +OPC_ORA_ABSY = $19 +;OPC_NOP = $1a; unofficial +OPC_SLO_ABSY = $1b; unofficial +OPC_NOP_ABSX = $1c; unofficial +OPC_ORA_ABSX = $1d +OPC_ASL_ABSX = $1e +OPC_SLO_ABSX = $1f; unofficial +OPC_JSR_ABS = $20 +OPC_AND_ZPXI = $21 +;OPC_JAM = $22; unofficial +OPC_RLA_ZPXI = $23; unofficial +OPC_BIT_ZP = $24 +OPC_AND_ZP = $25 +OPC_ROL_ZP = $26 +OPC_RLA_ZP = $27; unofficial +OPC_PLP = $28 +OPC_AND_IMM = $29 +OPC_ROL = $2a +;OPC_ANC_IMM = $2b; unofficial +OPC_BIT_ABS = $2c +OPC_AND_ABS = $2d +OPC_ROL_ABS = $2e +OPC_RLA_ABS = $2f; unofficial +OPC_BMI = $30 +OPC_AND_ZPIY = $31 +;OPC_JAM = $32; unofficial +OPC_RLA_ZPIY = $33; unofficial +;OPC_NOP_ZPX = $34 +OPC_AND_ZPX = $35 +OPC_ROL_ZPX = $36 +OPC_RLA_ZPX = $37; unofficial +OPC_SEC = $38 +OPC_AND_ABSY = $39 +;OPC_NOP = $3a; unofficial +OPC_RLA_ABSY = $3b; unofficial +;OPC_NOP_ABSX = $3c +OPC_AND_ABSX = $3d +OPC_ROL_ABSX = $3e +OPC_RLA_ABSX = $3f; unofficial +OPC_RTI = $40 +OPC_EOR_ZPXI = $41 +;OPC_JAM = $42; unofficial +OPC_SRE_ZPXI = $43; unofficial +OPC_NOP_ZP = $44 +OPC_EOR_ZP = $45 +OPC_LSR_ZP = $46 +OPC_SRE_ZP = $47; unofficial +OPC_PHA = $48 +OPC_EOR_IMM = $49 +OPC_LSR = $4a +OPC_ASL_IMM = $4b +OPC_JMP_ABS = $4c +OPC_EOR_ABS = $4d +OPC_LSR_ABS = $4e +OPC_SRE_ABS = $4f; unofficial +OPC_BVC = $50 +OPC_EOR_ZPIY = $51 +;OPC_JAM = $52; unofficial +OPC_SRE_ZPIY = $53; unofficial +;OPC_NOP_ZPX = $54; unofficial +OPC_EOR_ZPX = $55 +OPC_LSR_ZPX = $56 +OPC_SRE_ZPX = $57; unofficial +OPC_CLI = $58 +OPC_EOR_ABSY = $59 +;OPC_NOP = $5a +OPC_SRE_ABSY = $5b; unofficial +;OPC_NOP_ABSX = $5c +OPC_EOR_ABSX = $5d +OPC_LSR_ABSX = $5e +OPC_SRE_ABSX = $5f; unofficial +OPC_RTS = $60 +OPC_ADC_ZPIX = $61 +;OPC_JAM = $62; unofficial +OPC_RRA_ZPIX = $63; unofficial +;OPC_NOP_ZP = $64; unofficial +OPC_ADC_ZP = $65 +OPC_ROR_ZP = $66 +OPC_RRA_ZP = $67; unofficial +OPC_PLA = $68 +OPC_ADC_IMM = $69 +OPC_ROR = $6a +OPC_ARR_IMM = $6b; unofficial +OPC_JMP_ABSI = $6c +OPC_ADC_ABS = $6d +OPC_ROR_ABS = $6e +OPC_RRA_ABS = $6f; unofficial +OPC_BVS = $70 +OPC_ADC_ZPIY = $71 +;OPC_JAM = $72; unofficial +OPC_RRA_ZPIY = $73; unofficial +;OPC_NOP_ZPX = $74; unofficial +OPC_ADC_ZPX = $75 +OPC_ROR_ZPX = $76 +OPC_RRA_ZPX = $77; unofficial +OPC_SEI = $78 +OPC_ADC_ABSY = $79 +;OPC_NOP = $7a; unofficial +OPC_RRA_ABSY = $7b; unofficial +;OPC_NOP_ABSX = $7c; unofficial +OPC_ADC_ABSX = $7d +OPC_ROR_ABSX = $7e +OPC_RRA_ABSX = $7f; unofficial +OPC_NOP_IMM = $80; unofficial +OPC_STA_ZPXI = $81 +;OPC_NOP_IMM = $82 +OPC_SAX_ZPXI = $83; unofficial +OPC_STY_ZP = $84 +OPC_STA_ZP = $85 +OPC_STX_ZP = $86 +OPC_SAX_ZP = $87; unofficial +OPC_DEY = $88 +;OPC_NOP_IMM = $89; unofficial +OPC_TXA = $8a +OPC_XAA_IMM = $8b; unofficial +OPC_STY_ABS = $8c +OPC_STA_ABS = $8d +OPC_STX_ABS = $8e +OPC_SAX_ABS = $8f; unofficial +OPC_BCC = $90 +OPC_STA_ZPIY = $91 +;OPC_JAM = $92; unofficial +OPC_AHX_ZPIY = $93; unofficial +OPC_STY_ZPX = $94 +OPC_STA_ZPX = $95 +OPC_STY_ZPY = $96 +OPC_SAX_ZPY = $97; unofficial +OPC_TYA = $98 +OPC_STA_ABSY = $99 +OPC_TXS = $9a +OPC_TAS_ABSY = $9b; unofficial +OPC_SHF_ABSX = $9c; unofficial +OPC_STA_ABSX = $9d +OPC_SHX_ABSY = $9e; unofficial +OPC_AHX_ABSY = $9f; unofficial +OPC_LDY_IMM = $a0 +OPC_LDA_ZPXI = $a1 +OPC_LDX_IMM = $a2 +OPC_LAX_ZPXI = $a3; unofficial +OPC_LDY_ZP = $a4 +OPC_LDA_ZP = $a5 +OPC_LDX_ZP = $a6 +OPC_LAX_ZP = $a7 +OPC_TAY = $a8 +OPC_LDA_IMM = $a9 +OPC_TAX = $aa +OPC_LAX_IMM = $ab; unofficial +OPC_LDY_ABS = $ac +OPC_LDA_ABS = $ad +OPC_LDX_ABS = $ae +OPC_LAX_ABS = $af; unofficial +OPC_BCS = $b0 +OPC_LDA_ZPIY = $b1 +;OPC_JAM = $b2; unofficial +;OPC_LAX_ZPI = $b3; unofficial +OPC_LDY_ZPX = $b4 +OPC_LDA_ZPX = $b5 +OPC_LDX_ZPY = $b6 +OPC_LAX_ZPY = $b7; unofficial +OPC_CLV = $b8 +OPC_LDA_ABSY = $b9 +OPC_TSX = $ba +OPC_LAS_ABSY = $bb +OPC_LDY_ABSX = $bc +OPC_LDA_ABSX = $bd +OPC_LDX_ABSY = $be +OPC_LAX_ABSY = $bf; unofficial +OPC_CPY_IMM = $c0 +OPC_CMP_ZPXI = $c1 +;OPC_NOP_IMM = $c2; unofficial +OPC_DCP_ZPXI = $c3; unofficial +OPC_CPY_ZP = $c4 +OPC_CMP_ZP = $c5 +OPC_DEC_ZP = $c6 +OPC_DCP_ZP = $c7; unofficial +OPC_INY = $c8 +OPC_CMP_IMM = $c9 +OPC_DEX = $ca +OPC_SBX_IMM = $cb; unofficial +OPC_CPY_ABS = $cc +OPC_CMP_ABS = $cd +OPC_DEC_ABS = $ce +OPC_DCP_ABS = $cf; unofficial +OPC_BNE = $d0 +OPC_CMP_ZPIY = $d1 +;OPC_JAM = $d2; unofficial +OPC_DCP_ZPIY = $d3; unofficial +;OPC_NOP_ZPX = $d4; unofficial +OPC_CMP_ZPX = $d5 +OPC_DEC_ZPX = $d6 +OPC_DCP_ZPX = $d7; unofficial +OPC_CLD = $d8 +OPC_CMP_ABSY = $d9 +;OPC_NOP = $da; unofficial +OPC_DCP_ABSY = $db; unofficial +;OPC_NOP_ABSX = $dc; unofficial +OPC_CMP_ABSX = $dd +OPC_DEC_ABSX = $de +OPC_DCP_ABSX = $df; unofficial +OPC_CPX_IMM = $e0 +OPC_SBC_ZPXI = $e1 +;OPC_NOP_IMM = $e2; unofficial +OPC_ISC_ZPXI = $e3; unofficial +OPC_CPX_ZP = $e4 +OPC_SBC_ZP = $e5 +OPC_INC_ZP = $e6 +OPC_ISC_ZP = $e7; unofficial +OPC_INX = $e8 +OPC_SBC_IMM = $e9 +OPC_NOP = $ea +;OPC_SBC_IMM = $eb +OPC_CPX_ABS = $ec +OPC_SBC_ABS = $ed +OPC_INC_ABS = $ee +OPC_ISC_ABS = $ef; unofficial +OPC_BEQ = $f0 +OPC_SBC_ZPIY = $f1 +;OPC_JAM = $f2; unofficial +OPC_ISC_ZPIY = $f3; unofficial +;OPC_NOP_ZPX = $f4; unofficial +OPC_SBC_ZPX = $f5 +OPC_INC_ZPX = $f6 +OPC_ISC_ZPX = $f7; unofficial +OPC_SED = $f8 +OPC_SBC_ABSY = $f9 +;OPC_NOP = $fa; unofficial +OPC_ISC_ABSY = $fb; unofficial +;OPC_NOP_ABSX = $fc; unofficial +OPC_SBC_ABSX = $fd +OPC_INC_ABSX = $fe +OPC_ISC_ABSX = $ff; unofficial + +NEGATIVE = $80; N flag +OVERFLOW = $40; V flag +BREAK = $10; B flag +DECIMAL = $08; D flag +INTERRUPT = $04; I flag +ZERO = $02; Z flag +CARRY = $01; C flag + +FLAG_N = $80; N flag +FLAG_V = $40; V flag +FLAG_B = $10; B flag +FLAG_D = $08; D flag +FLAG_I = $04; I flag +FLAG_Z = $02; Z flag +FLAG_C = $01; C flag + +IO_PORT_DIRECTION = $00 +.if PLATFORM = 16 + IO_PORT_SERIAL_DATA_OUT_OUTPUT = %00000001 + IO_PORT_SERIAL_DATA_OUT_INPUT = %00000000 + IO_PORT_SERIAL_CLK_OUT_OUTPUT = %00000010 + IO_PORT_SERIAL_CLK_OUT_INPUT = %00000000 + IO_PORT_SERIAL_ATN_OUT_OUTPUT = %00000100 + IO_PORT_SERIAL_ATN_OUT_INPUT = %00000000 + IO_PORT_CST_MTR_OUTPUT = %00001000 + IO_PORT_CST_MTR_INPUT = %00000000 + IO_PORT_CST_RD_OUTPUT = %00010000 + IO_PORT_CST_RD_INPUT = %00000000 + IO_PORT_SERIAL_CLK_IN_OUTPUT = %01000000 + IO_PORT_SERIAL_CLK_IN_INPUT = %00000000 + IO_PORT_CST_WRT_INPUT = IO_PORT_SERIAL_CLK_IN_INPUT + IO_PORT_CST_WRT_OUTPUT = IO_PORT_SERIAL_CLK_IN_OUTPUT + IO_PORT_SERIAL_DATA_IN_OUTPUT = %10000000 + IO_PORT_SERIAL_DATA_IN_INPUT = %00000000 + IO_PORT_CST_SENSE_INPUT = IO_PORT_SERIAL_DATA_IN_INPUT + IO_PORT_CST_SENSE_OUTPUT = IO_PORT_SERIAL_DATA_IN_OUTPUT +.else + LORAM_OUTPUT = %00000001 + LORAM_INPUT = %00000000 + HIRAM_OUTPUT = %00000010 + HIRAM_INPUT = %00000000 + CHAREN_OUTPUT = %00000100 + CHAREN_INPUT = %00000000 + CASSETTE_WRITE_OUTPUT = %00001000 + CASSETTE_WRITE_INPUT = %00000000 + CASSETTE_SENSE_OUTPUT = %00010000 + CASSETTE_SENSE_INPUT = %00000000 + CASSETTE_MOTOR_OUTPUT = %00100000 + CASSETTE_MOTOR_INPUT = %00000000 + + IO_PORT_DIRECTION_DEFAULT = CASSETTE_MOTOR_OUTPUT | CASSETTE_SENSE_OUTPUT | CASSETTE_WRITE_OUTPUT | CHAREN_OUTPUT | HIRAM_OUTPUT | LORAM_OUTPUT +.endif + +IO_PORT = $01 +.if PLATFORM = 16 + IO_PORT_SERIAL_DATA_OUT = %00000001 + IO_PORT_CST_SENSE_OUT = %00000001 + IO_PORT_SERIAL_CLK_OUT = %00000010 + IO_PORT_CST_WRT_OUT = %00000010 + IO_PORT_SERIAL_ATN_OUT = %00000100 + IO_PORT_CST_MTR = %00001000 + IO_PORT_CST_MTR_OFF = IO_PORT_CST_MTR + IO_PORT_CST_MTR_ON = %00000000 + IO_PORT_CST_RD = %00010000 + IO_PORT_SERIAL_CLK_IN = %01000000 + IO_PORT_CST_WRT_IN = %01000000 + IO_PORT_SERIAL_DATA_IN = %10000000 + IO_PORT_CST_SENSE_IN = %10000000 +.else + CASSETTE_WRITE = %00001000 + CASSETTE_SENSE = %00010000 + CASSETTE_MOTOR = %00100000 + .if PLATFORM = 128 + CPU_D800_BANK_0 = %00000000 + CPU_D800_BANK_1 = %00000001 + VIC_D800_BANK_0 = %00000000 + VIC_D800_BANK_1 = %00000010 + CHARGEN_ENABLED = %00000000 + CHARGEN_DISABLED = %00000100 + CAPS_LOCK_OFF = %01000000; ASCII for German C-128 + CAPS_LOCK_ON = %00000000; DIN for German C-128 + + IO_PORT_CHARGEN_ENABLED = CAPS_LOCK_OFF | CASSETTE_SENSE | CASSETTE_MOTOR | CHARGEN_ENABLED | VIC_D800_BANK_1 | CPU_D800_BANK_1; $77 + IO_PORT_CHARGEN_DISABLED = CAPS_LOCK_OFF | CASSETTE_SENSE | CASSETTE_MOTOR | CHARGEN_DISABLED | VIC_D800_BANK_1 | CPU_D800_BANK_1; $73 + + .else + LORAM = %00000001 + HIRAM = %00000010 + CHAREN = %00000100 + + MEMCONFIG_IO_KERNAL_BASIC = CASSETTE_SENSE | CASSETTE_MOTOR | CHAREN | HIRAM | LORAM; $37 + MEMCONFIG_IO_KERNAL = CASSETTE_SENSE | CASSETTE_MOTOR | CHAREN | HIRAM | 0 ; $36 + MEMCONFIG_IO = CASSETTE_SENSE | CASSETTE_MOTOR | CHAREN | 0 | LORAM; $35 + MEMCONFIG_ALL_RAM_2 = CASSETTE_SENSE | CASSETTE_MOTOR | CHAREN | 0 | 0 ; $34 + MEMCONFIG_CHARGEN_KERNAL_BASIC = CASSETTE_SENSE | CASSETTE_MOTOR | 0 | HIRAM | LORAM; $33 + MEMCONFIG_CHARGEN_KERNAL = CASSETTE_SENSE | CASSETTE_MOTOR | 0 | HIRAM | 0 ; $32 + MEMCONFIG_CHARGEN = CASSETTE_SENSE | CASSETTE_MOTOR | 0 | 0 | LORAM; $31 + MEMCONFIG_ALL_RAM = CASSETTE_SENSE | CASSETTE_MOTOR | 0 | 0 | 0 ; $30 + .endif +.endif + +STACK = $0100 + +NMI_VECTOR = $fffa +NMI_VECTORLO = $fffa +NMI_VECTORHI = $fffb + +RESET_VECTOR = $fffc +RESET_VECTORLO = $fffc +RESET_VECTORHI = $fffd + +IRQ_VECTOR = $fffe +IRQ_VECTORLO = $fffe +IRQ_VECTORHI = $ffff + +.macro SKIPBYTE + .byte OPC_BIT_ZP +.endmacro + +.macro SKIPBYTE_NOP + .byte OPC_NOP_IMM +.endmacro + +.macro SKIPWORD + .byte OPC_BIT_ABS +.endmacro + +.macro SKIPWORD_NOP + .byte OPC_NOP_ABS +.endmacro + +.endif; !_CPU_INC_ diff --git a/shared/float.inc b/shared/float.inc new file mode 100644 index 0000000..239ea48 --- /dev/null +++ b/shared/float.inc @@ -0,0 +1,26 @@ + +.ifndef _FLOAT_INC_ +_FLOAT_INC_ = 1 + +.include "basic.inc" +.include "kernal.inc" + +.macro UINT16TOFAC + ldy #$00 + jsr INT24TOMANTISSA + sec + jsr NORMALIZE +.endmacro + +.macro INT32TOFAC arg + lda arg + $00 + ldx arg + $01 + ldy arg + $02 + jsr INT24TOMANTISSA + lda arg + $03 + sta FACHO + sec + jsr NORMALIZE +.endmacro + +.endif; FLOAT_INC diff --git a/shared/kernal.inc b/shared/kernal.inc new file mode 100755 index 0000000..4facd81 --- /dev/null +++ b/shared/kernal.inc @@ -0,0 +1,239 @@ + +.ifndef _KERNAL_INC_ +_KERNAL_INC_ = 1 + +DEVICE_SCREEN = $03 + +.if .defined(PLATFORM) & (PLATFORM = 128) + +BANK = $02 +PC = $03 +S_REG = $05 +A_REG = $06 +X_REG = $07 +Y_REG = $08 +TEMPST = $1b; temporary string descriptor stack + TEMPST_LEN = 0 + TEMPST_PTR = 1 +INDEX1 = $24; utility pointer area +VARTAB = $2f; start of BASIC variables +FRETOP = $35; pointer: bottom of string storage +FACEXP = $63; FAC exponent +FACHO = $64; FAC mantissa + +.else + +INDEX1 = $22; utility pointer area +VARTAB = $2d; start of BASIC variables +FRETOP = $33; pointer: bottom of string storage +FRESPC = $35; utility string pointer +FACEXP = $61; FAC exponent +FACHO = $62; FAC mantissa +STRDSC = $64; temporary string descriptor + STRDSC_LEN = 0 + STRDSC_PTR = 1 +FACSGN = $66; FAC sign + +.endif + +STATUS = $90 +VERCK = $93 + +CINV = $0314 +CBINV = $0316 + +C3PO = $94; serial bus output char buffered flag +BSOUR = $95; char buffer for serial bus + +.if .defined(PLATFORM) & (PLATFORM = 16) + +KERNAL_ROM = $8000 +KERNAL_ROM_SIZE = $8000 + +LDTND = $97; logical file index/open files count +DFLTN = $98; default input device +DFLTO = $99; default output device +MSGFLG = $9a +EAL = $9d; end address lo +EAH = $9e; end address hi +R2D2 = $a6; serial bus EOI flag +BSOUR1 = $a8; serial bus shift counter +COUNT = $aa; serial bus counter +FNLEN = $ab; length of current file name +LA = $ac; logical file number +SA = $ad; current secondary address +FA = $ae; current I/O device +FNADR = $af; current file name +MEMUSS = $b4; load ram base +PNT = $c8; current screen line address +PNTR = $ca; cursor column on current line +QTSW = $cb; quote switch +USE4DY = $f9 + +IBASIN = $0322; CHRIN vector +IBSOUT = $0324; CHROUT vector +ILOAD = $032e; LOAD vector + +COLOR = $053b + +PALETTE = $07f9 +PALETTE_DEFAULT = $ff +PALETTE_USER = $00 + +CLRSCR = $d88b +NOEOI = $e19f; send byte on serial bus +CHECKPARALLEL = $eda9; check if drive is parallel +LUKING = $f160 +LODING = $f189 +ERROR9 = $f28b +NLOAD = $f04a +KPREND = $fcc3; restore registers and return from IRQ handler + +.else; !(.defined(PLATFORM) & (PLATFORM = 16)) + +KERNAL_ROM = $e000 +KERNAL_ROM_SIZE = $2000 + +LDTND = $98; logical file index/open files count +DFLTN = $99; default input device +DFLTO = $9a; default output device +MSGFLG = $9d +R2D2 = $a3; serial bus EOI flag +BSOUR1 = $a4; serial bus shift counter +COUNT = $a5; serial bus counter +EAL = $ae; end address lo +EAH = $af; end address hi +FNLEN = $b7; length of current file name +LA = $b8; logical file number +SA = $b9; current secondary address +FA = $ba; current I/O device +FNADR = $bb; current file name +STAL = $c1; start address lo +STAH = $c2; start address hi +MEMUSS = $c3; load ram base + + .if .defined(PLATFORM) & (PLATFORM = 128) +FNBANK = $c7; filename bank +MODE = $d7 +PNT = $e0; current screen line address +USER = $e2; current screen line colour address +PNTR = $ec; cursor column on current line +COLOR = $f1 +QTSW = $f4; quote switch +BUF = $0200; BASIC and monitor input buffer +PALNTS = $0a03; 0: NTSC, 1: PAL +ENABL = $0a0f; RS232 activity flags +SERIAL = $0a1c +BLNON = $0a26; on/off blink flag +BLNSW = $0a27; cursor blink enable +BLNCT = $0a28; count to toggle cursor +GDBLN = $0a29; char under cursor +GDCOL = $0a2a; original colour under cursor +VM1 = $0a2c; VIC text screen and character base ($d018 shadow) + .else; !(.defined(PLATFORM) & (PLATFORM = 128)) +BLNSW = $cc; cursor blink enable +BLNCT = $cd; count to toggle cursor +GDBLN = $ce; char under cursor +BLNON = $cf; on/off blink flag +PNT = $d1; current screen line address +PNTR = $d3; cursor column on current line +QTSW = $d4; quote switch +LDTB1 = $d9 +USER = $f3; current screen line colour address + +COLOR = $0286 +GDCOL = $0287; original colour under cursor +ENABL = $02a1; RS232 activity flags +PALNTS = $02a6; 0: NTSC, 1: PAL + .endif; !(.defined(PLATFORM) & (PLATFORM = 128)) + +NMINV = $0318 +IOPEN = $031a +ICLOSE = $031c +ICHKIN = $031e +ICHKOUT = $0320 +ICLRCH = $0322 +IBASIN = $0324 +IBSOUT = $0326 +ISTOP = $0328 +IGETIN = $032a +ICLALL = $032c +USRCMD = $032e +ILOAD = $0330 +ISAVE = $0332 + + .if .defined(PLATFORM) & (PLATFORM = 128) +PRINT = $c72d +CURSORON = $cb21 +DSPP = $cc34; reset cursor blink count, set colour pointer, put a char on the screen +NOEOI = $e3ae; send byte on serial bus +LUKING = $f50f +LODING = $f533 +ERROR9 = $f694 +SETBANK = $f73f +KPREND = $ff33; restore registers and return from IRQ handler +SWAPPER = $ff5f +JSRFAR = $ff6e +FETCH = $ff74 + .else; !(.defined(PLATFORM) & (PLATFORM = 128)) +POLYX = $e059 +CLRSCR = $e544 +DSPP = $ea13; reset cursor blink count, set colour pointer, put a char on the screen +DSPP2 = $ea1c; put a char on the screen +SCOLOR = $ea24; synchronise colour pointer +KEY = $ea31; keyboard/cursor handler +KPREND = $ea7e; ack timer, restore registers and return from IRQ handler +ROWSLO = $ecf0 +NOEOI = $ed5a; send byte on serial bus +NLOAD = $f4a5 +LUKING = $f5af +LODING = $f5d2 +ERROR9 = $f713 + .endif; !(.defined(PLATFORM) & (PLATFORM = 128)) + +.endif; !(.defined(PLATFORM) & (PLATFORM = 16)) + + +SECND = $ff93 + SA_LISTEN = $20 + SA_UNLISTEN = $3f + SA_TALK = $40 + SA_UNTALK = $5f + SA_OPENCHANNEL = $60 + SA_CLOSE = $e0 + SA_OPEN = $f0 +TKSA = $ff96 +ACPTR = $ffa5 +CIOUT = $ffa8 +UNTLK = $ffab +UNLSN = $ffae +LISTN = $ffb1 +TALK = $ffb4 +READSS = $ffb7 + KERNAL_STATUS_EOF = %01000000 + KERNAL_STATUS_EOF_BURST = %00011111 + KERNAL_STATUS_ERROR_BURST = %00000010; >= 2 +SETLFS = $ffba + COMMAND_ERROR_CHANNEL = $0f +SETNAM = $ffbd +OPEN = $ffc0 + OPEN_TOOMANYFILES = $01 + OPEN_FILEOPEN = $02 + OPEN_FILENOTOPEN = $03 + OPEN_FILENOTFOUND = $04 + OPEN_DEVICENOTPRESENT = $05 + OPEN_NOTINPUTFILE = $06 + OPEN_NOTOUTPUTFILE = $07 + OPEN_MISSINGFILENAME = $08 + OPEN_ILLEGALDEVICENUMBER = $09 +CLOSE = $ffc3 +CHKIN = $ffc6 +CKOUT = $ffc9 +CLRCH = $ffcc +BASIN = $ffcf +BSOUT = $ffd2 +LOAD = $ffd5 +SAVE = $ffd8 +CLALL = $ffe7 + +.endif; !_KERNAL_INC_ diff --git a/shared/mmu.inc b/shared/mmu.inc new file mode 100644 index 0000000..bdb5ca7 --- /dev/null +++ b/shared/mmu.inc @@ -0,0 +1,36 @@ +; MOS8722 + +.ifndef _MMU_INC_ +_MMU_INC_ = 1 + +MMU_PCRA = $d501; Preconfiguration Register A +MMU_PCRB = $d502; Preconfiguration Register B +MMU_PCRC = $d503; Preconfiguration Register C +MMU_PCRD = $d504; Preconfiguration Register D + +MMU_RCR = $d506; RAM Configuration Register +VIC_BANK_0 = %00000000 +SHARED_RAM_LO = %00000100 +SHARED_RAM_1K = %00000000 +SHARED_RAM_4K = %00000001 +SHARED_RAM_8K = %00000010 +SHARED_RAM_16K = %00000011 + +MMU_CR = $ff00; Configuration Register +BANK_0 = %00000000 +BANK_1 = %01000000 +SYSTEM_ROM = %00000000 +HIGH_RAM = %00110000 ; $c000-$ffff +BASIC_HI = %00000000 +MID_RAM = %00001100 ; $8000-$bfff +BASIC_LO = %00000000 +LOW_RAM = %00000010 ; $4000-$7fff +IO_SPACE = %00000000 +RAM_ROM = %00000001 + +MMU_LCRA = $ff01; Load Configuration Register A +MMU_LCRB = $ff02; Load Configuration Register B +MMU_LCRC = $ff03; Load Configuration Register C +MMU_LCRD = $ff04; Load Configuration Register D + +.endif; !_MMU_INC_ diff --git a/shared/pio.inc b/shared/pio.inc new file mode 100644 index 0000000..cd82386 --- /dev/null +++ b/shared/pio.inc @@ -0,0 +1,10 @@ +; MOS6529 + +.ifndef _PIO_INC_ +_PIO_INC_ = 1 + +PIO_USER_PORT = $fd10 + +PIO_KEYBOARD_COLUMN = $fd30 + +.endif diff --git a/shared/standard.inc b/shared/standard.inc new file mode 100644 index 0000000..79e7f5c --- /dev/null +++ b/shared/standard.inc @@ -0,0 +1,194 @@ + +.ifndef _STANDARD_INC_ +_STANDARD_INC_ = 1 + + +.include "cpu.inc"; SKIPWORD + + +;; binary arithmetics + +.define IS_POWER_OF_2(value) (((value) & ((value) - 1)) = 0) + +.define ALIGN(value, align) (((value) + (align) - 1) & ~((align) - 1)) + +.define TOP_BIT(value) (-1 + ((value) >= $1) + ((value) >= $2) + ((value) >= $4) + ((value) >= $8) + ((value) >= $10) + ((value) >= $20) + ((value) >= $40) + ((value) >= $80) + ((value) >= $0100) + ((value) >= $0200) + ((value) >= $0400) + ((value) >= $0800) + ((value) >= $1000) + ((value) >= $2000) + ((value) >= $4000) + ((value) >= $8000)) + +; rounds up to nearest power of 2 +.define PAD(value) (1 + (((value) > $1) * $1) + (((value) > $2) * $2) + (((value) > $4) * $4) + (((value) > $8) * 8) + (((value) > $10) * $10) + (((value) > $20) * $20) + (((value) > $40) * $40) + (((value) > $80) * $80) + (((value) > $100) * $100) + (((value) > $200) * $200) + (((value) > $400) * $400) + (((value) > $800) * $800) + (((value) > $1000) * $1000) + (((value) > $2000) * $2000) + (((value) > $4000) * $4000) + (((value) > $8000) * $8000)) + + +;; subroutine calling using the stack + +; arguments are always pushed and popped with 16-bit sizes + +.macro PUSH_ARG value + .ifnblank value + .if (.match (.left (1, {value}), #)) + lda #<(.right (.tcount ({value}) - 1, {value})) + pha + lda #>(.right (.tcount ({value}) - 1, {value})) + pha + .else + lda value + 0 + pha + lda value + 1 + pha + .endif + .endif +.endmacro + +.macro POP_ARG + pla + tay + pla +.endmacro + +.macro CALL address, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 + .local postcall + + lda #>(postcall - 1) + pha + lda #<(postcall - 1) + pha + tsx + PUSH_ARG arg8 + PUSH_ARG arg7 + PUSH_ARG arg6 + PUSH_ARG arg5 + PUSH_ARG arg4 + PUSH_ARG arg3 + PUSH_ARG arg2 + PUSH_ARG arg1 + txa + pha + jmp address +postcall: +.endmacro + +.macro VARARGS_START + pla + tax +.endmacro + +.macro VARARGS_END + txs +.endmacro + + +;; memory manipulation + +.macro MEMSET address, size, value + + .ifref __memset_impl + CALL __memset_impl, address, size, value + .else + .local __post_memset_impl + + CALL __memset_impl, address, size, value; for .ifref detection + jmp __post_memset_impl + + ; this implementation is optimized for + ; fast inner loops at reasonable overhead + ; (only one write access per loop pass) + +__memset_impl: + ; not re-entrant + .scope + + VARARGS_START + POP_ARG + sta memsetlo + sty memsethi + POP_ARG + sta memsetsizelo + sty memsetsizehi + POP_ARG + sta memsetvalue + VARARGS_END + + ; check if region to fill starts on a page boundary +memsetlo = * + $01 + ldx #$00 + beq memsetpage + + ; if not, fill <256 bytes at the beginning of the region + sec + lda #$00 + sbc memsetlo; number of bytes until next page boundary + sec +memsetsizelo = * + $01 + sbc #$00 + tax + lda #$00 + sbc memsetsizehi + bcc :++ + + ; region is between two page boundaries + lda memsetlo + sta :+ + $01 + lda memsethi + sta :+ + $02 +: sta a:$00,x + dex + bne :- + rts; that's it + +: ; region stretches beyond at least one page boundary + lda memsethi + sta :+ + $02 + lda memsetvalue + ldx memsetlo +: sta a:$00,x + inx + bne :- + sec; update memsetsize + ;ldx #$00 + txa + sbc memsetlo + sta :+ + $01 + lda memsetsizelo +: sbc #$00 + sta memsetsizelo + bcs :+ + dec memsetsizehi +: + + ; fill whole pages +memsetpage: +memsetsizehi = * + $01 + ldy #$00 + beq memsetend + lda memsethi + sta :+ + $02 +memsetvalue = * + $01 + lda #$00 + ;ldx #$00 +: sta a:$00,x + inx + bne :- + inc :- + $02 + dey + bne :- + lda :- + $02 + SKIPWORD + +memsetend: ; fill remaining <256 bytes +memsethi = * + $01 + lda #$00 + sta :+ + $03 + lda memsetvalue + ldx memsetsizelo + inx +: dex + sta a:$00,x + bne :- + rts + + .endscope + +__post_memset_impl: + .endif; __memset_impl +.endmacro + +.endif; !_STANDARD_INC_ diff --git a/shared/ted.inc b/shared/ted.inc new file mode 100755 index 0000000..d53c048 --- /dev/null +++ b/shared/ted.inc @@ -0,0 +1,279 @@ +; MOS7360/8360 + +.ifndef _TED_INC_ +_TED_INC_ = 1 + + +CYCLES_PER_LINE_PAL = 57; single clock +CYCLES_PER_LINE_NTSC = 57; single clock + +DISPLAY_LINES_PAL = 312 +DISPLAY_LINES_NTSC = 262 + +VSYNC_LINE_PAL = 257 +VSYNC_LINE_NTSC = 229 + +SCREEN_COLUMNS = 40 +SCREEN_ROWS = 25 +SCREEN_SIZE = SCREEN_COLUMNS * SCREEN_ROWS + +BITMAP_BACKGROUND = 0 +BITMAP_SIZE = SCREEN_SIZE * 8 +.define MAKE_HIRES_COLOURS(one_bits, zero_bits) (((zero_bits) << 4) | ((one_bits) & $0f)) +.define MAKE_HIRES_INTENSITIES(one_bits, zero_bits) (((one_bits) & $f0) | ((zero_bits) >> 4)) + + +.if PLATFORM = 16 ; cia.inc defines C-64 and 1581 symbols, so it might be included along with, e.g., ted.inc +; keyboard matrix positions +STOP_COLUMN = %01111111 +STOP_ROW = %01111111 +LEFT_SHIFT_COLUMN = %11111101 +LEFT_SHIFT_ROW = %01111111 +ALL_COLUMNS = %00000000 +NO_ROWS = %11111111 +.endif + + +TED_ROM_BANK_BASE = $fdd0 +TED_ROM_BANK_0 = TED_ROM_BANK_BASE + 0 +TED_ROM_BANK_1 = TED_ROM_BANK_BASE + 1 +TED_ROM_BANK_2 = TED_ROM_BANK_BASE + 2 +TED_ROM_BANK_3 = TED_ROM_BANK_BASE + 3 +TED_ROM_BANK_4 = TED_ROM_BANK_BASE + 4 +TED_ROM_BANK_5 = TED_ROM_BANK_BASE + 5 +TED_ROM_BANK_6 = TED_ROM_BANK_BASE + 6 +TED_ROM_BANK_7 = TED_ROM_BANK_BASE + 7 +TED_ROM_BANK_8 = TED_ROM_BANK_BASE + 8 +TED_ROM_BANK_9 = TED_ROM_BANK_BASE + 9 +TED_ROM_BANK_10 = TED_ROM_BANK_BASE + 10 +TED_ROM_BANK_11 = TED_ROM_BANK_BASE + 11 +TED_ROM_BANK_12 = TED_ROM_BANK_BASE + 12 +TED_ROM_BANK_13 = TED_ROM_BANK_BASE + 13 +TED_ROM_BANK_14 = TED_ROM_BANK_BASE + 14 +TED_ROM_BANK_15 = TED_ROM_BANK_BASE + 15 + + +TED_BASE = $ff00 + +CHARSET_ADDR_UPPERGRAPHIC = $d000 +CHARSET_ADDR_UPPERLOWER = $d400 + +ROM_CHARSET_UPGFX = CHARSET_ADDR_UPPERGRAPHIC +ROM_CHARSET_UPLOW = CHARSET_ADDR_UPPERLOWER + +COLOUR_BLACK = $00 +COLOUR_WHITE = $01 +COLOUR_RED = $02 +COLOUR_CYAN = $03 +COLOUR_VIOLET = $04 +COLOUR_PURPLE = COLOUR_VIOLET +COLOUR_LILAC = COLOUR_VIOLET +COLOUR_GREEN = $05 +COLOUR_BLUE = $06 +COLOUR_YELLOW = $07 +COLOUR_ORANGE = $08 +COLOUR_BROWN = $09 +COLOUR_LIGHTRED = $0a + +INTENSITY_0 = $00 +INTENSITY_1 = $10 +INTENSITY_2 = $20 +INTENSITY_3 = $30 +INTENSITY_4 = $40 +INTENSITY_5 = $50 +INTENSITY_6 = $60 +INTENSITY_7 = $70 +BLINK = $80 + +COLOUR_DARKGREY = COLOUR_WHITE | INTENSITY_3 +COLOUR_MEDIUMGREY = COLOUR_WHITE | INTENSITY_4 + + +TED_COUNTER1_LO = TED_BASE + $00 +TED_COUNTER1_HI = TED_BASE + $01 + +TED_COUNTER2_LO = TED_BASE + $02 +TED_COUNTER2_HI = TED_BASE + $03 + +TED_COUNTER3_LO = TED_BASE + $04 +TED_COUNTER3_HI = TED_BASE + $05 + +TED_CTRL1 = TED_BASE + $06; Control register 1 + SCROLLY_0 = %00000000 + SCROLLY_1 = %00000001 + SCROLLY_2 = %00000010 + SCROLLY_3 = %00000011 + SCROLLY_4 = %00000100 + SCROLLY_5 = %00000101 + SCROLLY_6 = %00000110 + SCROLLY_7 = %00000111 + SCROLLY_MASK = %00000111 + LINES_24 = %00000000 + LINES_25 = %00001000 + DISPLAY_ENABLE = %00010000 + DISPLAY_DISABLE = %00000000 + TEXT_MODE = %00000000 + BITMAP_MODE = %00100000 + EBCM_MODE = %01000000 + INVALID_MODE = %01100000 + +TED_CTRL2 = TED_BASE + $07; Control register 2 + SCROLLX_0 = %00000000 + SCROLLX_1 = %00000001 + SCROLLX_2 = %00000010 + SCROLLX_3 = %00000011 + SCROLLX_4 = %00000100 + SCROLLX_5 = %00000101 + SCROLLX_6 = %00000110 + SCROLLX_7 = %00000111 + SCROLLX_MASK = %00000111 + COLUMNS_38 = %00000000 + COLUMNS_40 = %00001000 + SINGLECOLOUR_MODE = %00000000 + MULTICOLOUR_MODE = %00010000 + FREEZE = %00100000 + PAL_NTSC_MASK = %01000000 + NTSC = PAL_NTSC_MASK + PAL = %00000000 + +TED_KEYBOARD_LATCH = TED_BASE + $08 + +TED_IRR = TED_BASE + $09 + RASTER_IRQ = %00000010 + LIGHTPEN_IRQ = %00000100 + COUNTER_1_IRQ = %00001000 + COUNTER_2_IRQ = %00010000 + COUNTER_3_IRQ = %01000000 + +TED_IMR = TED_BASE + $0a + IRQ_RASTERLINE_MSB = %00000001 +;RASTER_IRQ = %00000010 +;LIGHTPEN_IRQ = %00000100 +;COUNTER_1_IRQ = %00001000 +;COUNTER_2_IRQ = %00010000 +;COUNTER_3_IRQ = %01000000 + +TED_IRQ_RASTERLINE = TED_BASE + $0b + +TED_CURSOR_HI = TED_BASE + $0c +TED_CURSOR_LO = TED_BASE + $0d + +TED_BITMAP_ADDR = TED_BASE + $12 + CHARSET_BITMAP_IN_ROM_MASK = %00000100 + CHARSET_BITMAP_IN_ROM = CHARSET_BITMAP_IN_ROM_MASK + CHARSET_BITMAP_IN_RAM = %00000000 + BITMAP_ADDR_MASK = %00111000 + BITMAP_ADDR_SHIFT = 3 + BITMAP_ADDR_RSHIFT = 10 + .define MAKE_BITMAP_ADDR(address) .lobyte(.lobyte((address) >> BITMAP_ADDR_RSHIFT) & BITMAP_ADDR_MASK) + +TED_CHARGEN_ADDR = TED_BASE + $13 + ROM_IS_ENABLED = %00000001 + FORCE_SINGLE_CLOCK = %00000010 + ALLOW_DOUBLE_CLOCK = %00000000 + CHARGEN_ADDR_MASK = %11111100 + CHARGEN_ADDR_RSHIFT = 8 + .define MAKE_CHARGEN_ADDR(address) .lobyte(.lobyte((address) >> CHARGEN_ADDR_RSHIFT) & CHARGEN_ADDR_MASK) + +TED_SCREEN_ADDR = TED_BASE + $14 + SCREEN_ADDR_MASK = %11111000 + SCREEN_ADDR_SHIFT = 3 + SCREEN_ADDR_RSHIFT = 8 + .define MAKE_SCREEN_ADDR(address) .lobyte(((address) >> SCREEN_ADDR_RSHIFT) & SCREEN_ADDR_MASK) + +TED_BGCOLOUR = TED_BASE + $15; Background colour +BGCOLOUR = TED_BGCOLOUR +TED_BGCOLOUR0 = TED_BGCOLOUR; Background colour 0 +TED_BGCOLOUR1 = TED_BASE + $16; Background colour 1 +TED_MULTICOLOUR0 = TED_BGCOLOUR1; Multicolour 1 +TED_BGCOLOUR2 = TED_BASE + $17; Background colour 2 +TED_MULTICOLOUR1 = TED_BGCOLOUR2; Multicolour 2 +TED_BGCOLOUR3 = TED_BASE + $18; Background colour 3 + +TED_BORDERCOLOUR = TED_BASE + $19; Overscan colour +BORDERCOLOUR = TED_BORDERCOLOUR + +TED_RASTERLINE_MSB = TED_BASE + $1c + TED_RASTERLINE_MSB_MASK = %00000001 + +TED_RASTERLINE = TED_BASE + $1d + RASTERLINE = TED_RASTERLINE +TED_RASTERCOLUMN = TED_BASE + $1e + +TED_VERTSUBCOUNT = TED_BASE + $1f + VERTSUBCOUNT_MASK = %00000111 + VERTSUBCOUNT_0 = %00000000 + VERTSUBCOUNT_1 = %00000001 + VERTSUBCOUNT_2 = %00000010 + VERTSUBCOUNT_3 = %00000011 + VERTSUBCOUNT_4 = %00000100 + VERTSUBCOUNT_5 = %00000101 + VERTSUBCOUNT_6 = %00000110 + VERTSUBCOUNT_7 = %00000111 + +TED_ROM_ENABLE = TED_BASE + $3e; the RAM/ROM state also determines where TED fetches screen chars, +TED_RAM_ENABLE = TED_BASE + $3f; but not bitmap data (always RAM) and chargen (register $12, bit 2) + + +.macro WAIT_VBL + .scope + .local loop + .local pal + .local done + +loop: lda TED_RASTERLINE + ldx TED_RASTERLINE_MSB + cmp TED_RASTERLINE + bne loop + bit TED_CTRL2 + bvc pal + cmp #.lobyte(VSYNC_LINE_NTSC) + bne loop + txa + and #%00000001 + cmp #.hibyte(VSYNC_LINE_NTSC) + bne loop + beq done +pal: cmp #.lobyte(VSYNC_LINE_PAL) + bne loop + txa + and #%00000001 + cmp #.hibyte(VSYNC_LINE_PAL) + bne loop +done: + .endscope +.endmacro + +.macro DISPLAY_HIRES_BITMAP bitmap, screen + WAIT_VBL + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta TED_CTRL1 + lda TED_BITMAP_ADDR + and #.lobyte(~(BITMAP_ADDR_MASK | CHARSET_BITMAP_IN_ROM)) + ora #MAKE_BITMAP_ADDR(bitmap) + sta TED_BITMAP_ADDR + lda TED_SCREEN_ADDR + and #.lobyte(~SCREEN_ADDR_MASK) + ora #MAKE_SCREEN_ADDR(screen) + sta TED_SCREEN_ADDR +.endmacro + +; requires forced slow clock +.macro STABILIZE_RASTER + .local delay + .local delays + + lda TED_RASTERCOLUMN + lsr + lsr + and #%00000111 + sta delay +delay = * + $01 + bpl delays +delays: lda #OPC_LDA_IMM + lda #OPC_LDA_IMM + lda #OPC_LDA_IMM + lda OPC_NOP +.endmacro + +.endif; !_TED_INC_ diff --git a/shared/vdc.inc b/shared/vdc.inc new file mode 100644 index 0000000..e6e4dfb --- /dev/null +++ b/shared/vdc.inc @@ -0,0 +1,15 @@ +; MOS8563/8568 + +.ifndef _VDC_INC_ +_VDC_INC_ = 1 + +VDC_SR = $d600; Status Register +VRT = %00100000; vertical retrace + +VDC_CR = $d600; Configuration Register +FG_BG = $1a; Foreground Colour/Background Colour +RED = $08 + +VDC_DR = $d601; Data Register + +.endif; !_VDC_INC_ diff --git a/shared/via.inc b/shared/via.inc new file mode 100755 index 0000000..0aa77ea --- /dev/null +++ b/shared/via.inc @@ -0,0 +1,386 @@ +; MOS6522 + +.ifndef _VIA_INC_ +_VIA_INC_ = 1 + +.ifdef CX500 +; The Century Planning Corp. CX-500/Tecmate NPH-501C drive is not a 1541 clone, as the +; VIAs are located at $3800 and $5c00 due to simplified address decoding logics, and the ROM +; including the position of the GCR encoding and decoding tables is entirely different as well. +VIA1_BASE = $3800 +.else +VIA1_BASE = $1800 +.endif + + VIA_PIO0 = %00000001 + VIA_PIO1 = %00000010 + VIA_PIO2 = %00000100 + VIA_PIO3 = %00001000 + VIA_PIO4 = %00010000 + VIA_PIO5 = %00100000 + VIA_PIO6 = %01000000 + VIA_PIO7 = %10000000 + +VIA1_PRB = VIA1_BASE + $00; Port register B +VIA1_ORB = VIA1_PRB; Output Register B +VIA1_IRB = VIA1_PRB; Input Register B + DATA_IN = VIA_PIO0 + DATA_OUT = VIA_PIO1 + CLK_IN = VIA_PIO2 + CLK_OUT = VIA_PIO3 + ATNA_OUT = VIA_PIO4 + DEVICE_NUMBER = VIA_PIO5 | VIA_PIO6 + DEVICE_NUMBER_8 = %00000000 + DEVICE_NUMBER_9 = VIA_PIO5 + DEVICE_NUMBER_10 = VIA_PIO6 + DEVICE_NUMBER_11 = VIA_PIO5 | VIA_PIO6 + DEVICE_NUMBER_SHIFT = 5 + DEVICE_NUMBER_MASK = DEVICE_NUMBER + ATN_IN = VIA_PIO7 + +; works only on 1571(CR) +VIA1_PRA = VIA1_BASE + $01; Port register A +VIA1_ORA = VIA1_PRA; Output Register A +VIA1_IRA = VIA1_PRA; Input Register A + TRACK_0 = %00000001 + FAST_SERIAL_DIR = %00000010 + FAST_SERIAL_INPUT = %00000000 + FAST_SERIAL_OUTPUT = FAST_SERIAL_DIR + SIDE_SELECT = %00000100 + SIDE_A = %00000000 + SIDE_B = SIDE_SELECT + TWO_MHZ = %00100000 + BYTE_READY = %10000000 + +VIA1_DDRB = VIA1_BASE + $02; Data direction register B + VIA_PIO0_OUTPUT = VIA_PIO0 + VIA_PIO0_INPUT = %00000000 + VIA_DATA_IN_OUTPUT = VIA_PIO0 + VIA_DATA_IN_INPUT = %00000000 + VIA_PIO1_OUTPUT = VIA_PIO1 + VIA_PIO1_INPUT = %00000000 + VIA_DATA_OUT_OUTPUT = VIA_PIO1 + VIA_DATA_OUT_INPUT = %00000000 + VIA_PIO2_OUTPUT = VIA_PIO2 + VIA_PIO2_INPUT = %00000000 + VIA_CLK_IN_OUTPUT = VIA_PIO2 + VIA_CLK_IN_INPUT = %00000000 + VIA_PIO3_OUTPUT = VIA_PIO3 + VIA_PIO3_INPUT = %00000000 + VIA_CLK_OUT_OUTPUT = VIA_PIO3 + VIA_CLK_OUT_INPUT = %00000000 + VIA_PIO4_OUTPUT = VIA_PIO4 + VIA_PIO4_INPUT = %00000000 + VIA_ATNA_OUT_OUTPUT = VIA_PIO4 + VIA_ATNA_OUT_INPUT = %00000000 + VIA_PIO5_OUTPUT = VIA_PIO5 + VIA_PIO5_INPUT = %00000000 + VIA_PIO6_OUTPUT = VIA_PIO6 + VIA_PIO6_INPUT = %00000000 + VIA_DEVICE_NUMBER_OUTPUT = VIA_PIO5 | VIA_PIO6 + VIA_DEVICE_NUMBER_INPUT = %00000000 + VIA_PIO7_OUTPUT = VIA_PIO7 + VIA_PIO7_INPUT = %00000000 + VIA_ATN_IN_OUTPUT = VIA_PIO7 + VIA_ATN_IN_INPUT = %00000000 + +VIA1_DDRA = VIA1_BASE + $03; Data direction register A + +VIA1_T1C_L = VIA1_BASE + $04; Timer 1 Lo +VIA1_T1_LO = VIA1_T1C_L + +VIA1_T1C_H = VIA1_BASE + $05; Timer 1 Hi +VIA1_T1_HI = VIA1_T1C_H + +VIA1_T1L_L = VIA1_BASE + $06; Timer 1 Latch Lo +VIA1_T1_LATCH_LO = VIA1_T1L_L + +VIA1_T1L_H = VIA1_BASE + $07; Timer 1 Latch Hi +VIA1_T1_LATCH_HI = VIA1_T1L_H + +VIA1_T2C_L = VIA1_BASE + $08; Timer 2 Lo +VIA1_T2_LO = VIA1_T2C_L + +VIA1_T2C_H = VIA1_BASE + $09; Timer 2 Hi +VIA1_T2_HI = VIA1_T2C_H + +VIA1_SR = VIA1_BASE + $0a; Shift Register + +VIA1_ACR = VIA1_BASE + $0b; Auxiliary Control Register + PA_LATCHING_ENABLE = %00000001 + PA_LATCHING_ENABLED = PA_LATCHING_ENABLE + PA_LATCHING_DISABLE = %00000000 + PA_LATCHING_DISABLED = PA_LATCHING_DISABLE + PB_LATCHING_ENABLE = %00000010 + PB_LATCHING_ENABLED = PB_LATCHING_ENABLE + PB_LATCHING_DISABLE = %00000000 + PB_LATCHING_DISABLED = PB_LATCHING_DISABLE + SHIFT_REG_CONTROL = %00011100 + SHIFT_DISABLE = %00000000 + SHIFT_DISABLED = SHIFT_DISABLE + SHIFT_IN_T2 = %00000100 + SHIFT_IN_02 = %00001000 + SHIFT_IN_EXTCLK = %00001100 + SHIFT_OUT_FREERUN_T2 = %00010000 + SHIFT_OUT_T2 = %00010100 + SHIFT_OUT_02 = %00011000 + SHIFT_OUT_EXTCLK = %00011100 + SHIFT_REG_CONTROL_SHIFT = 2 + SHIFT_REG_CONTROL_MASK = SHIFT_REG_CONTROL + T2_CONTROL = %00100000 + T2_TIMED_INTERRUPT = %00000000 + T2_COUNTDOWN_PULSE_PB6 = T2_CONTROL + T1_CONTROL = %11000000 + T1_ONE_SHOT = %00000000 + T1_FREE_RUNNING = %01000000 + T1_ONE_SHOT_PB7_OUT = %10000000 + T1_FREE_RUNNING_INVERT_PB7_OUT = %11000000 + T1_CONTROL_SHIFT = 6 + T1_CONTROL_MASK = T1_CONTROL + +VIA1_PCR = VIA1_BASE + $0c; Peripheral Control Register + CA1_INTERRUPT_CONTROL = %00000001 + CA1_IRQ_ON_POS_ACTIVE_EDGE = CA1_INTERRUPT_CONTROL + CA1_IRQ_ON_NEG_ACTIVE_EDGE = %00000000 + ATN_IN_IRQ_ON_POS_ACTIVE_EDGE = CA1_IRQ_ON_POS_ACTIVE_EDGE + ATN_IN_IRQ_ON_NEG_ACTIVE_EDGE = CA1_IRQ_ON_NEG_ACTIVE_EDGE + CA2_INTERRUPT_CONTROL = %00001110 + CA2_INPUT_IRQ_NEG_ACT_EDGE = %00000000 + CA2_INPUT_IND_IRQ_NEG_EDGE = %00000010 + CA2_INPUT_IRQ_POS_ACT_EDGE = %00000100 + CA2_INPUT_IND_IRQ_POS_EDGE = %00000110 + CA2_OUTPUT_HANDSHAKE = %00001000 + CA2_OUTPUT_PULSE = %00001010 + CA2_OUTPUT_LOW = %00001100 + CA2_OUTPUT_HIGH = %00001110 + CA2_INTERRUPT_CONTROL_SHIFT = 1 + CA2_INTERRUPT_CONTROL_MASK = CB2_INTERRUPT_CONTROL + CB1_INTERRUPT_CONTROL = %00010000 + CB1_IRQ_ON_POS_ACTIVE_EDGE = CB1_INTERRUPT_CONTROL + CB1_IRQ_ON_NEG_ACTIVE_EDGE = %00000000 + CB2_INTERRUPT_CONTROL = %11100000 + CB2_INPUT_IRQ_NEG_ACT_EDGE = %00000000 + CB2_INPUT_IND_IRQ_NEG_EDGE = %00100000 + CB2_INPUT_IRQ_POS_ACT_EDGE = %01000000 + CB2_INPUT_IND_IRQ_POS_EDGE = %01100000 + CB2_OUTPUT_HANDSHAKE = %10000000 + CB2_OUTPUT_PULSE = %10100000 + CB2_OUTPUT_LOW = %11000000 + CB2_OUTPUT_HIGH = %11100000 + CB2_INTERRUPT_CONTROL_SHIFT = 5 + CB2_INTERRUPT_CONTROL_MASK = CB2_INTERRUPT_CONTROL + +VIA1_IFR = VIA1_BASE + $0d; Interrupt Flag Register + IRQ_CA2_ACTIVE_EDGE = %00000001 + IRQ_CA1_ACTIVE_EDGE = %00000010 + IRQ_ATN_ASSERTED = IRQ_CA1_ACTIVE_EDGE + IRQ_SHIFT_REG = %00000100 + IRQ_CB2_ACTIVE_EDGE = %00001000 + IRQ_CB1_ACTIVE_EDGE = %00010000 + IRQ_TIMER_2 = %00100000 + IRQ_TIMER_1 = %01000000 + IRQS_PENDING = %10000000 + +VIA1_IER = VIA1_BASE + $0e; Interrupt Enable Register +;IRQ_CA2_ACTIVE_EDGE = %00000001 +;IRQ_CA1_ACTIVE_EDGE = %00000010 +;IRQ_SHIFT_REG = %00000100 +;IRQ_CB2_ACTIVE_EDGE = %00001000 +;IRQ_CB1_ACTIVE_EDGE = %00010000 +;IRQ_TIMER_2 = %00100000 +;IRQ_TIMER_1 = %01000000 + IRQ_ALL_FLAGS = %01111111 + IRQ_SET_FLAGS = %10000000 + IRQ_CLEAR_FLAGS = %00000000 + +VIA1_PRA_NO_HANDSHAKE = VIA1_BASE + $0f; Port register A without Handshake +VIA1_ORA_NO_HANDSHAKE = VIA1_PRA_NO_HANDSHAKE; Output Register A without Handshake +VIA1_IRA_NO_HANDSHAKE = VIA1_PRA_NO_HANDSHAKE; Input Register A without Handshake + + +.ifdef CX500 +VIA2_BASE = $5c00 +.else +VIA2_BASE = $1c00 +.endif + +VIA2_PRB = VIA2_BASE + $00; Port register B +VIA2_ORB = VIA2_PRB; Output Register B +VIA2_IRB = VIA2_PRB; Input Register B + TRACK_STEP = VIA_PIO0 | VIA_PIO1 + TRACK_STEP_SHIFT = 0 + TRACK_STEP_MASK = TRACK_STEP + MOTOR = VIA_PIO2 + BUSY_LED = VIA_PIO3 + WRITE_PROTECT = VIA_PIO4 + BITRATE = VIA_PIO5 | VIA_PIO6 + BITRATE_SHIFT = 5 + BITRATE_MASK = BITRATE + SYNC_MARK = VIA_PIO7 + +VIA2_PRA = VIA2_BASE + $01; Port register A +VIA2_ORA = VIA2_PRA; Output Register A +VIA2_IRA = VIA2_PRA; Input Register A + +VIA2_DDRB = VIA2_BASE + $02; Data direction register B +;VIA_PIO0_OUTPUT = VIA_PIO0 +;VIA_PIO0_INPUT = %00000000 +;VIA_PIO1_OUTPUT = VIA_PIO1 +;VIA_PIO1_INPUT = %00000000 + VIA_TRACK_STEP_OUTPUT = VIA_PIO0 | VIA_PIO1 + VIA_TRACK_STEP_INPUT = %00000000 +;VIA_PIO2_OUTPUT = VIA_PIO2 +;VIA_PIO2_INPUT = %00000000 + VIA_MOTOR_OUTPUT = VIA_PIO2 + VIA_MOTOR_INPUT = %00000000 +;VIA_PIO3_OUTPUT = VIA_PIO3 +;VIA_PIO3_INPUT = %00000000 + VIA_BUSY_LED_OUTPUT = VIA_PIO3 + VIA_BUSY_LED_INPUT = %00000000 +;VIA_PIO4_OUTPUT = VIA_PIO4 +;VIA_PIO4_INPUT = %00000000 + VIA_WRITE_PROTECT_OUTPUT = VIA_PIO4 + VIA_WRITE_PROTECT_INPUT = %00000000 +;VIA_PIO5_OUTPUT = VIA_PIO5 +;VIA_PIO5_INPUT = %00000000 +;VIA_PIO6_OUTPUT = VIA_PIO6 +;VIA_PIO6_INPUT = %00000000 + VIA_BITRATE_OUTPUT = VIA_PIO5 | VIA_PIO6 + VIA_BITRATE_INPUT = %00000000 +;VIA_PIO7_OUTPUT = VIA_PIO7 +;VIA_PIO7_INPUT = %00000000 + VIA_SYNC_MARK_OUTPUT = VIA_PIO7 + VIA_SYNC_MARK_INPUT = %00000000 + +VIA2_DDRA = VIA2_BASE + $03; Data direction register A + +VIA2_T1C_L = VIA2_BASE + $04; Timer 1 Lo +VIA2_T1_LO = VIA2_T1C_L + +VIA2_T1C_H = VIA2_BASE + $05; Timer 1 Hi +VIA2_T1_HI = VIA2_T1C_H + +VIA2_T1L_L = VIA2_BASE + $06; Timer 1 Latch Lo +VIA2_T1_LATCH_LO = VIA2_T1L_L + +VIA2_T1L_H = VIA2_BASE + $07; Timer 1 Latch Hi +VIA2_T1_LATCH_HI = VIA2_T1L_H + +VIA2_T2C_L = VIA2_BASE + $08; Timer 2 Lo +VIA2_T2_LO = VIA2_T2C_L + +VIA2_T2C_H = VIA2_BASE + $09; Timer 2 Hi +VIA2_T2_HI = VIA2_T2C_H + +VIA2_SR = VIA2_BASE + $0a; Shift Register + +VIA2_ACR = VIA2_BASE + $0b; Auxiliary Control Register +;PA_LATCHING_ENABLE = %00000001 +;PA_LATCHING_ENABLED = PA_LATCHING_ENABLE +;PA_LATCHING_DISABLED = %00000000 +;PB_LATCHING_ENABLE = %00000010 +;PB_LATCHING_ENABLED = PB_LATCHING_ENABLE +;PB_LATCHING_DISABLED = %00000000 +;SHIFT_REG_CONTROL = %00011100 +;SHIFT_DISABLED = %00000000 +;SHIFT_IN_T2 = %00000100 +;SHIFT_IN_02 = %00001000 +;SHIFT_IN_EXTCLK = %00001100 +;SHIFT_OUT_FREERUN_T2 = %00010000 +;SHIFT_OUT_T2 = %00010100 +;SHIFT_OUT_02 = %00011000 +;SHIFT_OUT_EXTCLK = %00011100 +;SHIFT_REG_CONTROL_SHIFT = 2 +;SHIFT_REG_CONTROL_MASK = SHIFT_REG_CONTROL +;T2_CONTROL = %00100000 +;T2_TIMED_INTERRUPT = %00000000 +;T2_COUNTDOWN_PULSE_PB6 = T2_CONTROL +;T1_CONTROL = %11000000 +;T1_ONE_SHOT = %00000000 +;T1_FREE_RUNNING = %01000000 +;T1_ONE_SHOT_PB7_OUT = %10000000 +;T1_FREE_RUNNING_INVERT_PB7_OUT = %11000000 +;T1_CONTROL_SHIFT = 6 +;T1_CONTROL_MASK = T1_CONTROL + +VIA2_PCR = VIA2_BASE + $0c; Peripheral Control Register +;CA1_INTERRUPT_CONTROL = %00000001 +;CA1_IRQ_ON_POS_ACTIVE_EDGE = CA1_INTERRUPT_CONTROL +;CA1_IRQ_ON_NEG_ACTIVE_EDGE = %00000000 +BYTE_READY_IRQ_ON_POS_ACTIVE_EDGE = CA1_IRQ_ON_POS_ACTIVE_EDGE +BYTE_READY_IRQ_ON_NEG_ACTIVE_EDGE = CA1_IRQ_ON_NEG_ACTIVE_EDGE +;CA2_INTERRUPT_CONTROL = %00001110 +;CA2_INPUT_IRQ_NEG_ACT_EDGE = %00000000 +;CA2_INPUT_IND_IRQ_NEG_EDGE = %00000010 +;CA2_INPUT_IRQ_POS_ACT_EDGE = %00000100 +;CA2_INPUT_IND_IRQ_POS_EDGE = %00000110 +;CA2_OUTPUT_HANDSHAKE = %00001000 +;CA2_OUTPUT_PULSE = %00001010 +;CA2_OUTPUT_LOW = %00001100 +;CA2_OUTPUT_HIGH = %00001110 +BYTE_SYNC_DISABLE = CA2_OUTPUT_LOW +BYTE_SYNC_DISABLED = BYTE_SYNC_DISABLE +BYTE_SYNC_ENABLE = CA2_OUTPUT_HIGH +BYTE_SYNC_ENABLED = BYTE_SYNC_ENABLE +;CA2_INTERRUPT_CONTROL_SHIFT = 1 +;CA2_INTERRUPT_CONTROL_MASK = CB2_INTERRUPT_CONTROL +;CB1_INTERRUPT_CONTROL = %00010000 +;CB1_IRQ_ON_POS_ACTIVE_EDGE = CB1_INTERRUPT_CONTROL +;CB1_IRQ_ON_NEG_ACTIVE_EDGE = %00000000 +;CB2_INTERRUPT_CONTROL = %11100000 +;CB2_INPUT_IRQ_NEG_ACT_EDGE = %00000000 +;CB2_INPUT_IND_IRQ_NEG_EDGE = %00100000 +;CB2_INPUT_IRQ_POS_ACT_EDGE = %01000000 +;CB2_INPUT_IND_IRQ_POS_EDGE = %01100000 +;CB2_OUTPUT_HANDSHAKE = %10000000 +;CB2_OUTPUT_PULSE = %10100000 +;CB2_OUTPUT_LOW = %11000000 +;CB2_OUTPUT_HIGH = %11100000 +WRITE_MODE = CB2_OUTPUT_LOW +READ_MODE = CB2_OUTPUT_HIGH +;CB2_INTERRUPT_CONTROL_SHIFT = 5 +;CB2_INTERRUPT_CONTROL_MASK = CB2_INTERRUPT_CONTROL + +VIA2_IFR = VIA2_BASE + $0d; Interrupt Flag Register +;IRQ_CA2_ACTIVE_EDGE = %00000001 +;IRQ_CA1_ACTIVE_EDGE = %00000010 +;IRQ_SHIFT_REG = %00000100 +;IRQ_CB2_ACTIVE_EDGE = %00001000 +;IRQ_CB1_ACTIVE_EDGE = %00010000 +;IRQ_TIMER_2 = %00100000 +;IRQ_TIMER_1 = %01000000 +;IRQS_PENDING = %10000000 + +VIA2_IER = VIA2_BASE + $0e; Interrupt Enable Register +;IRQ_CA2_ACTIVE_EDGE = %00000001 +;IRQ_CA1_ACTIVE_EDGE = %00000010 +;IRQ_SHIFT_REG = %00000100 +;IRQ_CB2_ACTIVE_EDGE = %00001000 +;IRQ_CB1_ACTIVE_EDGE = %00010000 +;IRQ_TIMER_2 = %00100000 +;IRQ_TIMER_1 = %01000000 +;IRQ_ALL_FLAGS = %01111111 +;IRQ_SET_FLAGS = %10000000 +;IRQ_CLEAR_FLAGS = %00000000 + +VIA2_PRA_NO_HANDSHAKE = VIA2_BASE + $0f; Port register A without Handshake +VIA2_ORA_NO_HANDSHAKE = VIA2_PRA_NO_HANDSHAKE; Output Register A without Handshake +VIA2_IRA_NO_HANDSHAKE = VIA2_PRA_NO_HANDSHAKE; Input Register A without Handshake + + +; the VIA in the CMD FD + +VIA_BASE = $4000 + +VIA_PRA = VIA_BASE + $01; Port register A + +VIA_T1C_L = VIA_BASE + $04; Timer 1 Lo +VIA_T1C_H = VIA_BASE + $05; Timer 1 Hi + +VIA_T2C_H = VIA_BASE + $09; Timer 2 Hi +VIA_SR = VIA_BASE + $0a; Shift Register +VIA_ACR = VIA_BASE + $0b; Auxiliary Control Register + +VIA_IER = VIA_BASE + $0e; Interrupt Enable Register + +.endif; !_VIA_INC_ diff --git a/shared/vic.inc b/shared/vic.inc new file mode 100644 index 0000000..5da1637 --- /dev/null +++ b/shared/vic.inc @@ -0,0 +1,234 @@ +; MOS6567/MOS6569 + +.ifndef _VIC_INC_ +_VIC_INC_ = 1 + + +.include "cia.inc" + + +CYCLES_PER_LINE_PAL = 63 +CYCLES_PER_LINE_NTSC = 64 + +DISPLAY_LINES_PAL = 312 +DISPLAY_LINES_NTSC = 263 + +VSYNC_LINE_PAL = 0 +VSYNC_LINE_NTSC = 20 + +SCREEN_COLUMNS = 40 +SCREEN_ROWS = 25 +SCREEN_SIZE = SCREEN_COLUMNS * SCREEN_ROWS + +BITMAP_BACKGROUND = 0 +BITMAP_SIZE = SCREEN_SIZE * 8 +.define MAKE_HIRES_COLOURS(one_bits, zero_bits) (((zero_bits) << 4) | (one_bits)) + +SPRITE_WIDTH = 24 +SPRITE_POINTERS = $03f8 +.define MAKE_SPRITE_POINTER(address) (.lobyte((address) >> 6)) + +VIC2_IDLE_PATTERN = $3fff + + +VIC2_BASE = $d000 + +CHARSET_ADDR_UPPERGRAPHIC = $d000 +CHARSET_ADDR_UPPERLOWER = $d800 + +ROM_CHARSET_UPGFX = CHARSET_ADDR_UPPERGRAPHIC +ROM_CHARSET_UPLOW = CHARSET_ADDR_UPPERLOWER + +VIC2_COLOURRAM = $d800 + +COLOUR_BLACK = $00 +COLOUR_WHITE = $01 +COLOUR_RED = $02 +COLOUR_CYAN = $03 +COLOUR_VIOLET = $04 +COLOUR_PURPLE = COLOUR_VIOLET +COLOUR_LILAC = COLOUR_VIOLET +COLOUR_GREEN = $05 +COLOUR_BLUE = $06 +COLOUR_YELLOW = $07 +COLOUR_ORANGE = $08 +COLOUR_BROWN = $09 +COLOUR_LIGHTRED = $0a +COLOUR_DARKGREY = $0b +COLOUR_MEDIUMGREY = $0c +COLOUR_LIGHTGREEN = $0d +COLOUR_LIGHTBLUE = $0e +COLOUR_LIGHTGREY = $0f + +SPRITE_0 = %00000001 +SPRITE_1 = %00000010 +SPRITE_2 = %00000100 +SPRITE_3 = %00001000 +SPRITE_4 = %00010000 +SPRITE_5 = %00100000 +SPRITE_6 = %01000000 +SPRITE_7 = %10000000 + +SPRITE0_X = VIC2_BASE + $00; Sprite 0, x-coordinate bits 0-7 +SPRITE0_Y = VIC2_BASE + $01; Sprite 0, y-coordinate +SPRITE1_X = VIC2_BASE + $02; Sprite 1, x-coordinate bits 0-7 +SPRITE1_Y = VIC2_BASE + $03; Sprite 1, y-coordinate +SPRITE2_X = VIC2_BASE + $04; Sprite 2, x-coordinate bits 0-7 +SPRITE2_Y = VIC2_BASE + $05; Sprite 2, y-coordinate +SPRITE3_X = VIC2_BASE + $06; Sprite 3, x-coordinate bits 0-7 +SPRITE3_Y = VIC2_BASE + $07; Sprite 3, y-coordinate +SPRITE4_X = VIC2_BASE + $08; Sprite 4, x-coordinate bits 0-7 +SPRITE4_Y = VIC2_BASE + $09; Sprite 4, y-coordinate +SPRITE5_X = VIC2_BASE + $0a; Sprite 5, x-coordinate bits 0-7 +SPRITE5_Y = VIC2_BASE + $0b; Sprite 5, y-coordinate +SPRITE6_X = VIC2_BASE + $0c; Sprite 6, x-coordinate bits 0-7 +SPRITE6_Y = VIC2_BASE + $0d; Sprite 6, y-coordinate +SPRITE7_X = VIC2_BASE + $0e; Sprite 7, x-coordinate bits 0-7 +SPRITE7_Y = VIC2_BASE + $0f; Sprite 7, y-coordinate + +SPRITES_X_MSB = VIC2_BASE + $10; Sprites 0-7, x-coordinates bit 8 + +VIC2_CTRL1 = VIC2_BASE + $11; Control register 1 + SCROLLY_0 = %00000000 + SCROLLY_1 = %00000001 + SCROLLY_2 = %00000010 + SCROLLY_3 = %00000011 + SCROLLY_4 = %00000100 + SCROLLY_5 = %00000101 + SCROLLY_6 = %00000110 + SCROLLY_7 = %00000111 + SCROLLY_MASK = %00000111 + LINES_24 = %00000000 + LINES_25 = %00001000 + DISPLAY_ENABLE = %00010000 + DISPLAY_DISABLE = %00000000 + BITMAP_MODE = %00100000 + TEXT_MODE = %00000000 + EBCM_MODE = %01000000 + RASTERLINE_BIT8 = %10000000 + RASTERLINE_BIT8_SHIFT = 7 + RASTERLINE_MSB = %10000000 + RASTERLINE_MSB_SHIFT = 7 + +VIC2_RASTERLINE = VIC2_BASE + $12; Current raster line, raster IRQ trigger line + RASTERLINE = VIC2_RASTERLINE + MAX_RASTERLINE_PAL = $0137 + MAX_RASTERLINE_NTSC = $0105 + +VIC2_STROBE_X = VIC2_BASE + $13; Light pen x-position +VIC2_STROBE_Y = VIC2_BASE + $14; Light pen y-position + +VIC2_SPR_ENABLE = VIC2_BASE + $15; Enabled sprites + +VIC2_CTRL2 = VIC2_BASE + $16; Control register 2 + SCROLLX_0 = %00000000 + SCROLLX_1 = %00000001 + SCROLLX_2 = %00000010 + SCROLLX_3 = %00000011 + SCROLLX_4 = %00000100 + SCROLLX_5 = %00000101 + SCROLLX_6 = %00000110 + SCROLLX_7 = %00000111 + SCROLLX_MASK = %00000111 + COLUMNS_38 = %00000000 + COLUMNS_40 = %00001000 + SINGLECOLOUR_MODE = %00000000 + MULTICOLOUR_MODE = %00010000 + +VIC2_SPR_EXPANDY = VIC2_BASE + $17; y-expanded sprites + +VIC2_ADDR = VIC2_BASE + $18; Character set and bitmap locations + SCREEN_MASK = %11110000 + CHARSET_UPPERGRAPHIC = %00000100; $d000 + CHARSET_UPPERLOWER = %00000110; $d800 + CHARSET_MASK = %00001110 + BITMAP_MASK = %00001000 +.define VIC2_MAKE_ADDR(screen, charset_or_bitmap) .lobyte(((screen & $3fff) >> 6) | ((charset_or_bitmap & $3fff) >> 10)) + +VIC2_IRR = VIC2_BASE + $19; Interrupt Request Register + RASTER_IRQ = %00000001 + SPR_BGR_COLL_IRQ = %00000010 + SPR_SPR_COLL_IRQ = %00000100 + LIGHTPEN_IRQ = %00001000 + IRQ_FLAG = %10000000 + +VIC2_IMR = VIC2_BASE + $1a; Interrupt Mask Register + NO_INTERRUPTS = %00000000 +;RASTER_IRQ = %00000001 +;SPR_BGR_COLL_IRQ = %00000010 +;SPR_SPR_COLL_IRQ = %00000100 +;LIGHTPEN_IRQ = %00001000 + +VIC2_BG_PRIORITY = VIC2_BASE + $1b; Sprite-to-background priority + +VIC2_SPR_MCOLOUR = VIC2_BASE + $1c; Sprites multicolour enabled + +VIC2_SPR_EXPANDX = VIC2_BASE + $1d; Sprites x-expansion + +VIC2_SPR_COLL = VIC2_BASE + $1e; Sprite-to-sprite collision +VIC2_SPR_BG_COLL = VIC2_BASE + $1f; Sprite-to-background collision + +VIC2_BORDERCOLOUR = VIC2_BASE + $20; Overscan colour +BORDERCOLOUR = VIC2_BORDERCOLOUR + +VIC2_BGCOLOUR = VIC2_BASE + $21; Background colour +BGCOLOUR = VIC2_BGCOLOUR +VIC2_BGCOLOUR0 = VIC2_BGCOLOUR; Background colour 0 +VIC2_BGCOLOUR1 = VIC2_BASE + $22; Background colour 1 +VIC2_MULTICOLOUR0 = VIC2_BGCOLOUR1; Multicolour 1 +VIC2_BGCOLOUR2 = VIC2_BASE + $23; Background colour 2 +VIC2_MULTICOLOUR1 = VIC2_BGCOLOUR2; Multicolour 2 +VIC2_BGCOLOUR3 = VIC2_BASE + $24; Background colour 3 + +VIC2_SPR_MCOLOUR0 = VIC2_BASE + $25; Sprite multicolour 0 +VIC2_SPR_MCOLOUR1 = VIC2_BASE + $26; Sprite multicolour 1 + +VIC2_SPR0_COLOUR = VIC2_BASE + $27; Sprite colour 0 +VIC2_SPR1_COLOUR = VIC2_BASE + $28; Sprite colour 1 +VIC2_SPR2_COLOUR = VIC2_BASE + $29; Sprite colour 2 +VIC2_SPR3_COLOUR = VIC2_BASE + $2a; Sprite colour 3 +VIC2_SPR4_COLOUR = VIC2_BASE + $2b; Sprite colour 4 +VIC2_SPR5_COLOUR = VIC2_BASE + $2c; Sprite colour 5 +VIC2_SPR6_COLOUR = VIC2_BASE + $2d; Sprite colour 6 +VIC2_SPR7_COLOUR = VIC2_BASE + $2e; Sprite colour 7 + +VIC2_C128_EXT_KB = VIC2_BASE + $2f; Expanded keyboard matrix line +C128_EXT_KB = VIC2_C128_EXT_KB +VIC2_C128_CLOCK = VIC2_BASE + $30; Extended clock control +C128_CLOCK = VIC2_C128_CLOCK + C128_TWO_MHZ = %00000001 + C128_INC_LINECNTR = %00000010 + + +; c = 0: NTSC, c = 1: PAL +.macro WAIT_VBL + .scope + .local loop + + lda #VSYNC_LINE_NTSC + bcc loop + lda #VSYNC_LINE_PAL +loop: cmp VIC2_RASTERLINE + bne loop + bit VIC2_CTRL1 + bmi loop + cmp VIC2_RASTERLINE + bne loop + .endscope +.endmacro + +.macro DISPLAY_HIRES_BITMAP bitmap, screen + WAIT_VBL + lda #BITMAP_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3 + sta VIC2_CTRL1 + lda #SINGLECOLOUR_MODE | COLUMNS_40 | SCROLLX_0 + sta VIC2_CTRL2 + lda #VIC2_MAKE_ADDR(screen, bitmap) + sta VIC2_ADDR + lda CIA2_PRA + and #.lobyte(~VIC2_BANK_MASK) + ora #VIC2_MAKE_BANK(bitmap) + sta CIA2_PRA +.endmacro + +.endif; !_VIC_INC_